name: QA Main action

on:
  push:
    branches:
      - master
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  noInputsTest:
    name: >
      No inputs
    strategy:
      fail-fast: false
      matrix:
        os: [github-ubuntu-latest-s, macos-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Run action without args
        uses: ./
        env:
          SONAR_HOST_URL: http://not_actually_used
          SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
      - name: Assert
        run: |
          ./test/assertFileContains ./output.properties "sonar.projectBaseDir=."
  argsInputTest:
    name: >
      'args' input
    strategy:
      fail-fast: false
      matrix:
        os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Run action with args
        uses: ./
        with:
          args: -Dsonar.someArg=aValue -Dsonar.anotherArgWithSpaces="Another Value" -Dsonar.argWithSingleQuotes='Another Value'
        env:
          SONAR_HOST_URL: http://not_actually_used
          SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
      - name: Assert
        run: |
          ./test/assertFileContains ./output.properties "sonar.someArg=aValue"
          ./test/assertFileContains ./output.properties 'sonar.anotherArgWithSpaces="Another Value"'
          ./test/assertFileContains ./output.properties "sonar.argWithSingleQuotes='Another Value'"
  argsInputInjectionTest:
    name: >
      'args' input with command injection will fail
    strategy:
      fail-fast: false
      matrix:
        os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
        args:
          [
            -Dsonar.someArg=aValue && echo "Injection",
            -Dsonar.someArg="value\"; whoami; echo \"",
          ]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Run action with args
        id: runTest
        uses: ./
        continue-on-error: true
        with:
          args: ${{ matrix.args }}
        env:
          SONAR_HOST_URL: http://not_actually_used
          SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
      - name: Fail if action succeeded
        if: steps.runTest.outcome == 'success'
        run: exit 1
      - name: Assert the scanner was not called
        run: |
          ./test/assertFileDoesntExist ./output.properties
  backtickCommandInjectionTest:
    name: >
      'args' input with backticks injection does not execute command
    strategy:
      fail-fast: false
      matrix:
        os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Run action with args
        uses: ./
        continue-on-error: true
        with:
          args: >
            -Dsonar.arg1="refs/heads/branch: [workflows] Bump `actions/*`" -Dsonar.arg2="test `echo Command Injection`" -Dsonar.arg3="`id`" -Dsonar.arg4="test'; `echo injection`; echo '" -Dsonar.arg5="   `whoami`   " -Dsonar.arg6="test\`echo injection\`test"
        env:
          SONAR_HOST_URL: http://not_actually_used
          SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
      - name: Assert command in arg is not executed
        run: |
          ./test/assertFileContains ./output.properties 'sonar.arg1="refs/heads/branch\\: \[workflows\] Bump `actions/\*`"'
          ./test/assertFileContains ./output.properties 'sonar.arg2="test `echo Command Injection`"'
          ./test/assertFileContains ./output.properties 'sonar.arg3="`id`"'
          ./test/assertFileContains ./output.properties "sonar.arg4=\"test'; \`echo injection\`; echo '\""
          ./test/assertFileContains ./output.properties 'sonar.arg5="   `whoami`   "'
          ./test/assertFileContains ./output.properties 'sonar.arg6="test\\\\`echo injection\\\\`test"'
  dollarSymbolCommandInjectionTest:
    name: >
      'args' input with dollar command injection does not execute command
    strategy:
      matrix:
        os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Run action with args
        uses: ./
        continue-on-error: true
        with:
          args: -Dsonar.arg1="$(whoami)" -Dsonar.arg2="$GITHUB_TOKEN" -Dsonar.arg3="$(echo outer $(echo inner))" -Dsonar.arg4="value\$(whoami)end" -Dsonar.arg5="$(printf 'A%.0s' {1..10000})" -Dsonar.arg6='value"; $(whoami); echo "'
        env:
          SONAR_HOST_URL: http://not_actually_used
          SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
      - name: Assert command in arg is not executed
        run: |
          ./test/assertFileContains ./output.properties 'sonar.arg1="$(whoami)"'
          ./test/assertFileContains ./output.properties 'sonar.arg2="$GITHUB_TOKEN"'
          ./test/assertFileContains ./output.properties 'sonar.arg3="$(echo outer $(echo inner))"'
          ./test/assertFileContains ./output.properties 'sonar.arg4="value\\\\$(whoami)end"'
          ./test/assertFileContains ./output.properties 'sonar.arg5="$(printf '\''A%.0s'\'' {1..10000})"'
          ./test/assertFileContains ./output.properties 'sonar.arg6='\''value"; $(whoami); echo "'\'''
  otherCommandInjectionVariantsTest:
    name: >
      'args' input with other command injection variants does not execute command
    strategy:
      matrix:
        os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Run action with args
        uses: ./
        continue-on-error: true
        with:
          args: -Dsonar.arg1="test | base64" -Dsonar.arg2="value; whoami" -Dsonar.arg3="value && echo test" -Dsonar.arg4="value > /tmp/output.txt" -Dsonar.arg5="< /etc/passwd" -Dsonar.arg6="" -Dsonar.arg7="../../../*" -Dsonar.arg8="*.key" -Dsonar.arg9="test\u0027\u0060whoami\u0060"
        env:
          SONAR_HOST_URL: http://not_actually_used
          SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
      - name: Assert command in arg is not executed
        run: |
          ./test/assertFileContains ./output.properties 'sonar.arg1="test | base64"'
          ./test/assertFileContains ./output.properties 'sonar.arg2="value; whoami"'
          ./test/assertFileContains ./output.properties 'sonar.arg3="value && echo test"'
          ./test/assertFileContains ./output.properties 'sonar.arg4="value > /tmp/output.txt"'
          ./test/assertFileContains ./output.properties 'sonar.arg5="< /etc/passwd"'
          ./test/assertFileContains ./output.properties 'sonar.arg6=""'
          ./test/assertFileContains ./output.properties 'sonar.arg7="../../../\*"'
          ./test/assertFileContains ./output.properties 'sonar.arg8="\*.key"'
          ./test/assertFileContains ./output.properties 'sonar.arg9="test\\\\u0027\\\\u0060whoami\\\\u0060"'
  projectBaseDirInputTest:
    name: >
      'projectBaseDir' input
    strategy:
      matrix:
        os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - run: mkdir -p ./baseDir
      - name: Run action with projectBaseDir
        uses: ./
        with:
          args: -Dsonar.scanner.internal.dumpToFile=./output.properties
          projectBaseDir: ./baseDir
        env:
          SONAR_HOST_URL: http://not_actually_used
          SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
      - name: Assert
        run: |
          ./test/assertFileContains ./output.properties "sonar.projectBaseDir=.*/baseDir"
  scannerVersionTest:
    name: >
      'scannerVersion' input
    runs-on: github-ubuntu-latest-s # assumes default RUNNER_ARCH for linux is X64
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Run action with scannerVersion
        uses: ./
        with:
          scannerVersion: 6.1.0.4477
          args: -Dsonar.scanner.internal.dumpToFile=./output.properties
        env:
          NO_CACHE: true # force install-sonar-scanner-cli.sh execution
          SONAR_HOST_URL: http://not_actually_used
          SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
      - name: Assert
        run: |
          # Verify the tool was installed by checking it's in PATH
          if ! command -v sonar-scanner &> /dev/null; then
            echo "Error: sonar-scanner not found in PATH"
            exit 1
          fi
  scannerBinariesUrlTest:
    name: >
      'scannerBinariesUrl' input with invalid URL
    runs-on: github-ubuntu-latest-s # assumes default RUNNER_ARCH for linux is X64
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Run action with scannerBinariesUrl
        id: runTest
        uses: ./
        continue-on-error: true
        with:
          scannerVersion: 6.2.1.4610
          scannerBinariesUrl: https://invalid_uri/Distribution/sonar-scanner-cli
        env:
          NO_CACHE: true # force install-sonar-scanner-cli.sh execution
          SONAR_HOST_URL: http://not_actually_used
          SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
      - name: Fail if action succeeded
        if: steps.runTest.outcome == 'success'
        run: exit 1
      - name: Assert Sonar Scanner CLI was not downloaded
        run: |
          ./test/assertFileDoesntExist "$RUNNER_TEMP/sonarscanner/sonar-scanner-cli-6.2.1.4610-linux-x64.zip"
      - name: Assert Sonar Scanner CLI was not executed
        run: |
          ./test/assertFileDoesntExist ./output.properties
  scannerBinariesUrlIsEscapedWithWget:
    name: >
      'scannerBinariesUrl' is escaped with wget so special chars are not injected in the download command
    runs-on: github-ubuntu-latest-s
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Run action with scannerBinariesUrl
        id: runTest
        uses: ./
        continue-on-error: true
        with:
          scannerBinariesUrl: "http://some_uri;touch file.txt;"
        env:
          NO_CACHE: true
          SONAR_HOST_URL: http://not_actually_used
          SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output1.properties"}'
      - name: Assert file.txt does not exist
        run: |
          ./test/assertFileDoesntExist "$RUNNER_TEMP/sonarscanner/file.txt"
  scannerBinariesUrlIsEscapedWithCurl:
    name: >
      'scannerBinariesUrl' is escaped with curl so special chars are not injected in the download command
    runs-on: github-ubuntu-latest-s
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Remove wget
        run: sudo apt-get remove -y wget
      - name: Assert wget is not available
        run: |
          if command -v wget 2>&1 >/dev/null
          then
            exit 1
          fi
      - name: Run action with scannerBinariesUrl
        id: runTest
        uses: ./
        continue-on-error: true
        with:
          scannerBinariesUrl: "http://some_uri http://another_uri'; touch file.txt;"
        env:
          NO_CACHE: true
          SONAR_HOST_URL: http://not_actually_used
          SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output1.properties"}'
      - name: Assert file.txt does not exist
        run: |
          ./test/assertFileDoesntExist "$RUNNER_TEMP/sonarscanner/file.txt"
  dontFailGradleTest:
    name: >
      Don't fail on Gradle project
    runs-on: github-ubuntu-latest-s
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Run action on Gradle project
        id: runTest
        uses: ./
        continue-on-error: true
        env:
          SONAR_HOST_URL: http://not_actually_used
          SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
        with:
          projectBaseDir: ./test/gradle-project
          args: -Dsonar.scanner.internal.dumpToFile=./output.properties
      - name: Assert
        run: |
          ./test/assertFileExists ./output.properties
  dontFailGradleKotlinTest:
    name: >
      Don't fail on Kotlin Gradle project
    runs-on: github-ubuntu-latest-s
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Run action on Kotlin Gradle project
        id: runTest
        uses: ./
        continue-on-error: true
        env:
          SONAR_HOST_URL: http://not_actually_used
          SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
        with:
          projectBaseDir: ./test/gradle-project
          args: -Dsonar.scanner.internal.dumpToFile=./output.properties
      - name: Assert
        run: |
          ./test/assertFileExists ./output.properties
  dontFailMavenTest:
    name: >
      Don't fail on Maven project
    runs-on: github-ubuntu-latest-s
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Run action on Maven project
        id: runTest
        uses: ./
        continue-on-error: true
        env:
          SONAR_HOST_URL: http://not_actually_used
          SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
        with:
          projectBaseDir: ./test/maven-project
          args: -Dsonar.scanner.internal.dumpToFile=./output.properties
      - name: Assert
        run: |
          ./test/assertFileExists ./output.properties
  runAnalysisTest:
    runs-on: github-ubuntu-latest-s
    services:
      sonarqube:
        image: sonarqube:lts-community
        ports:
          - 9000:9000
        volumes:
          - sonarqube_data:/opt/sonarqube/data
          - sonarqube_logs:/opt/sonarqube/logs
          - sonarqube_extensions:/opt/sonarqube/extensions
        options: >-
          --health-cmd "grep -Fq \"SonarQube is operational\" /opt/sonarqube/logs/sonar.log"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 10
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Run action on sample project
        id: runTest
        uses: ./
        env:
          SONAR_HOST_URL: http://localhost:9000
        with:
          args: -Dsonar.login=admin -Dsonar.password=admin
          projectBaseDir: ./test/example-project
      - name: Assert
        run: |
          ./test/assertFileExists ./test/example-project/.scannerwork/report-task.txt
  runnerDebugUsedTest:
    name: >
      'RUNNER_DEBUG' is used
    strategy:
      fail-fast: false
      matrix:
        os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Run action with debug mode
        uses: ./
        with:
          args: -Dsonar.scanner.internal.dumpToFile=./output.properties
        env:
          RUNNER_DEBUG: 1
          SONAR_HOST_URL: http://not_actually_used
          SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output.properties"}'
      - name: Assert
        run: |
          ./test/assertFileContains ./output.properties "sonar.verbose=true"
  runAnalysisWithCacheTest:
    runs-on: github-ubuntu-latest-s
    services:
      sonarqube:
        image: sonarqube:lts-community
        ports:
          - 9000:9000
        volumes:
          - sonarqube_data:/opt/sonarqube/data
          - sonarqube_logs:/opt/sonarqube/logs
          - sonarqube_extensions:/opt/sonarqube/extensions
        options: >-
          --health-cmd "grep -Fq \"SonarQube is operational\" /opt/sonarqube/logs/sonar.log"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 10
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: SonarQube Cache
        uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
        with:
          path: ${{ github.workspace }}/.sonar/cache
          key: ${{ runner.os }}-${{ runner.arch }}-sonar
      - name: Run action on sample project
        id: runTest
        uses: ./
        env:
          SONAR_HOST_URL: http://localhost:9000
          SONAR_USER_HOME: ${{ github.workspace }}/.sonar
        with:
          args: -Dsonar.login=admin -Dsonar.password=admin
          projectBaseDir: ./test/example-project
      - name: Assert
        run: |
          ./test/assertFileExists ./test/example-project/.scannerwork/report-task.txt
  overrideSonarcloudUrlTest:
    name: >
      'SONARCLOUD_URL' is used
    strategy:
      fail-fast: false
      matrix:
        os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Run action with SONARCLOUD_URL
        uses: ./
        with:
          args: -Dsonar.scanner.apiBaseUrl=api.mirror.sonarcloud.io -Dsonar.scanner.internal.dumpToFile=./output.properties
        env:
          SONARCLOUD_URL: mirror.sonarcloud.io
          SONAR_TOKEN: FAKE_TOKEN
      - name: Assert
        run: |
          ./test/assertFileContains ./output.properties "sonar.host.url=mirror.sonarcloud.io"
          ./test/assertFileContains ./output.properties "sonar.scanner.sonarcloudUrl=mirror.sonarcloud.io"
  curlPerformsRedirect:
    name: >
      curl performs redirect when scannerBinariesUrl returns 3xx
    runs-on: github-ubuntu-latest-s
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Remove wget
        run: sudo apt-get remove -y wget
      - name: Assert wget is not available
        run: |
          if command -v wget 2>&1 >/dev/null
          then
            exit 1
          fi
      - name: Generate SSL certificates for nginx
        run: ./generate-ssl.sh
        working-directory: .github/qa-nginx-redirecting
      - name: Start nginx via Docker Compose
        run: docker compose up -d --wait
        working-directory: .github/qa-nginx-redirecting
      - name: Run action with scannerBinariesUrl
        id: runTest
        uses: ./
        with:
          scannerVersion: 6.2.1.4610
          scannerBinariesUrl: https://localhost:8080/clientRedirectToSonarBinaries
        env:
          NO_CACHE: true
          NODE_TLS_REJECT_UNAUTHORIZED: 0
          SONAR_HOST_URL: http://not_actually_used
          SONAR_SCANNER_JSON_PARAMS: '{"sonar.scanner.internal.dumpToFile": "./output1.properties"}'
      - name: Assert Sonar Scanner CLI was downloaded
        run: |
          # Verify the tool was installed by checking it's in PATH
          if ! command -v sonar-scanner &> /dev/null; then
            echo "Error: sonar-scanner not found in PATH"
            exit 1
          fi
  useSslCertificate:
    name: >
      'SONAR_ROOT_CERT' is converted to truststore
    strategy:
      fail-fast: false
      matrix:
        os: [github-ubuntu-latest-s, github-windows-latest-s, macos-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Run action with SSL certificate
        uses: ./
        with:
          args: -Dsonar.scanner.internal.dumpToFile=./output.properties
        env:
          SONAR_ROOT_CERT: |
            -----BEGIN CERTIFICATE-----
            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
            Fct6d1S08JAosVnZcP2P7Yz+TbmDRtsqCgk=
            -----END CERTIFICATE-----
          SONAR_HOST_URL: http://not_actually_used
      - name: Assert
        run: |
          ./test/assertFileExists ~/.sonar/ssl/truststore.p12
  analysisWithSslCertificate:
    name: >
      Analysis takes into account 'SONAR_ROOT_CERT'
    runs-on: github-ubuntu-latest-s
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Generate server certificate
        run: |
          openssl req \
            -newkey rsa:4096 \
            -x509 \
            -sha256 \
            -addext "subjectAltName = DNS:localhost" \
            -days 3650 \
            -nodes \
            -out server.crt \
            -subj "/C=CH/ST=Geneva/L=Geneva/O=Server/OU=Dept" \
            -keyout server.key
        working-directory: .github/qa-sq-behind-ngix
      - name: Start nginx and SonarQube via Docker Compose
        run: docker compose up -d --wait
        working-directory: .github/qa-sq-behind-ngix
      - name: Read correct server certificate
        run: |
          # read server.crt from .github/qa-sq-behind-ngix/ and store into the SONAR_ROOT_CERT_VALID 
          # environment variable, to be able to read it in the next step
          {
            echo 'SONAR_ROOT_CERT_VALID<<=========='
            cat .github/qa-sq-behind-ngix/server.crt
            echo ==========
          } >> $GITHUB_ENV
      - name: Run action with the correct SSL certificate
        uses: ./
        env:
          SONAR_ROOT_CERT: ${{ env.SONAR_ROOT_CERT_VALID }}
          SONAR_HOST_URL: https://localhost:4443
        with:
          args: -Dsonar.login=admin -Dsonar.password=admin
          projectBaseDir: ./test/example-project
      - name: Clear imported SSL certificates
        run: |
          rm -f ~/.sonar/ssl/truststore.p12
      - name: Run action with an invalid SSL certificate
        id: invalid_ssl_certificate
        continue-on-error: true
        uses: ./
        env:
          SONAR_ROOT_CERT: |
            -----BEGIN CERTIFICATE-----
            INVALID
            -----END CERTIFICATE-----
          SONAR_HOST_URL: https://localhost:4443
        with:
          args: -Dsonar.login=admin -Dsonar.password=admin
          projectBaseDir: ./test/example-project
      - name: Assert failure of previous step
        if: steps.invalid_ssl_certificate.outcome == 'success'
        run: exit 1
      - name: Clear imported SSL certificates
        run: |
          rm -f ~/.sonar/ssl/truststore.p12
      - name: Run action with the wrong SSL certificate
        id: wrong_ssl_certificate
        continue-on-error: true
        uses: ./
        env:
          SONAR_ROOT_CERT: |
            -----BEGIN CERTIFICATE-----
            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
            -----END CERTIFICATE-----
          SONAR_HOST_URL: https://localhost:4443
        with:
          args: -Dsonar.login=admin -Dsonar.password=admin
          projectBaseDir: ./test/example-project
      - name: Assert failure of previous step
        if: steps.wrong_ssl_certificate.outcome == 'success'
        run: exit 1
  updateTruststoreWhenPresent: # can happen in uncleaned self-hosted runners
    name: >
      truststore.p12 is updated when present
    runs-on: github-ubuntu-latest-s
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Create SONAR_SSL_FOLDER with a file in it (not-truststore.p12)
        run: |
          SONAR_SSL_FOLDER=~/.sonar/ssl
          mkdir -p "$SONAR_SSL_FOLDER"
          touch "$SONAR_SSL_FOLDER/not-truststore.p12"
          # emit SONAR_SSL_FOLDER to be able to read it in the next steps
          echo "SONAR_SSL_FOLDER=$SONAR_SSL_FOLDER" >> $GITHUB_ENV
      - name: Assert truststore.p12 does not file exists
        run: |
          [ ! -f "$SONAR_SSL_FOLDER/truststore.p12" ] || exit 1
      - name: Run action with SONAR_ROOT_CERT
        uses: ./
        env:
          # NO_CACHE not needed, as SONAR_SSL_FOLDER is setup when the Sonar Scanner is run, not installed
          SONAR_HOST_URL: http://not_actually_used
          SONAR_ROOT_CERT: |
            -----BEGIN CERTIFICATE-----
            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
            -----END CERTIFICATE-----
        with:
          args: -Dsonar.scanner.internal.dumpToFile=./output.properties
      - name: Assert not-truststore.p12 file still exists
        run: |
          [ -f "$SONAR_SSL_FOLDER/not-truststore.p12" ] || exit 1
      - name: Assert truststore.p12 file now exists and take note of modification time
        run: |
          [ -f "$SONAR_SSL_FOLDER/truststore.p12" ] || exit 1
          # emit the modification time of the truststore.p12 file to be able to read it in the next steps
          TRUSTSTORE_P12_MOD_TIME_T1=$(stat -c %Y "$SONAR_SSL_FOLDER/truststore.p12")
          echo "TRUSTSTORE_P12_MOD_TIME_T1=$TRUSTSTORE_P12_MOD_TIME_T1" >> $GITHUB_ENV
      - name: Run action a second time with a different SONAR_ROOT_CERT
        uses: ./
        env:
          # NO_CACHE not needed, as SONAR_SSL_FOLDER is setup when the Sonar Scanner is run, not installed
          SONAR_HOST_URL: http://not_actually_used
          SONAR_ROOT_CERT: |
            -----BEGIN CERTIFICATE-----
            MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMC
            Tk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYD
            VQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG
            9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4
            MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xi
            ZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2Zl
            aWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5v
            MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LO
            NoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHIS
            KOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d
            1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8
            BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7n
            bK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2Qar
            Q4/67OZfHd7R+POBXhophSMv1ZOo
            -----END CERTIFICATE-----
        with:
          args: -Dsonar.scanner.internal.dumpToFile=./output.properties
      - name: Assert truststore.p12 still exists, but it has been updated, and take note of modification time
        run: |
          [ -f "$SONAR_SSL_FOLDER/truststore.p12" ] || exit 1
          TRUSTSTORE_P12_MOD_TIME_T2=$(stat -c %Y "$SONAR_SSL_FOLDER/truststore.p12")
          [ "$TRUSTSTORE_P12_MOD_TIME_T1" != "$TRUSTSTORE_P12_MOD_TIME_T2" ] || exit 1
          # emit the modification time of the truststore.p12 file to be able to read it in the next steps
          echo "TRUSTSTORE_P12_MOD_TIME_T2=$TRUSTSTORE_P12_MOD_TIME_T2" >> $GITHUB_ENV
      - name: Remove sonar alias from truststore.p12
        run: keytool -delete -alias sonar -keystore "$SONAR_SSL_FOLDER/truststore.p12" -storepass changeit
      - name: Run action a third time
        uses: ./
        env:
          # NO_CACHE not needed, as SONAR_SSL_FOLDER is setup when the Sonar Scanner is run, not installed
          SONAR_HOST_URL: http://not_actually_used
          SONAR_ROOT_CERT: |
            -----BEGIN CERTIFICATE-----
            MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMC
            Tk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYD
            VQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG
            9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4
            MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xi
            ZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2Zl
            aWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5v
            MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LO
            NoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHIS
            KOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d
            1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8
            BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7n
            bK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2Qar
            Q4/67OZfHd7R+POBXhophSMv1ZOo
            -----END CERTIFICATE-----
        with:
          args: -Dsonar.scanner.internal.dumpToFile=./output.properties
      - name: Assert truststore.p12 still exists, and it has been updated again
        run: |
          [ -f "$SONAR_SSL_FOLDER/truststore.p12" ] || exit 1
          TRUSTSTORE_P12_MOD_TIME_T3=$(stat -c %Y "$SONAR_SSL_FOLDER/truststore.p12")
          [ "$TRUSTSTORE_P12_MOD_TIME_T2" != "$TRUSTSTORE_P12_MOD_TIME_T3" ] || exit 1
  scannerVersionValidationTest:
    name: >
      'scannerVersion' input validation
    runs-on: github-ubuntu-latest-s
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Run action with invalid scannerVersion
        id: invalid_version
        uses: ./
        continue-on-error: true
        with:
          scannerVersion: "7.1.0-SNAPSHOT"
          args: -Dsonar.scanner.internal.dumpToFile=./output.properties
        env:
          NO_CACHE: true
          SONAR_HOST_URL: http://not_actually_used
      - name: Assert failure of previous step
        if: steps.invalid_version.outcome == 'success'
        run: |
          echo "Action with invalid scannerVersion should have failed but succeeded"
          exit 1
