Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
249 changes: 249 additions & 0 deletions .github/workflows/sonarcloud.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
name: SonarCloud

on:
workflow_call:
inputs:
branch:
description: Branch or ref to analyse
required: false
type: string
version:
description: Application version
required: false
type: string
working-directory:
description: Project working directory
required: false
type: string
default: '.'
server-path:
description: Path containing the server Gradle wrapper (relative to working-directory)
required: false
type: string
default: 'server'
project_java_version:
description: Java version used to compile and test the application
required: false
type: string
default: '17'
node_version:
description: Node.js version required by the project
required: false
type: string
default: '20.x'
REGISTRY_URL:
description: Only used by older versions of Genesis Framework
required: false
type: string
SCOPE:
description: NPM scope used with REGISTRY_URL
required: false
type: string
sonar-inputs-artifact:
description: >-
Optional artifact name containing classes/coverage/test results from a prior build.
When set, prepare-analysis skips re-running tests and reuses this artifact.
required: false
type: string
default: ''
secrets:
GPR_READ_TOKEN:
required: true
GRADLE_PROPERTIES:
required: true
JFROG_USERNAME:
required: true
JFROG_EMAIL:
required: true
JFROG_PASSWORD:
required: true
JENKINSGENESIS_SONAR:
required: true

env:
NODE_AUTH_TOKEN: ${{ secrets.GPR_READ_TOKEN }}
SONAR_ARTIFACT_NAME: ${{ inputs.sonar-inputs-artifact != '' && inputs.sonar-inputs-artifact || format('sonar-inputs-{0}', github.run_id) }}
SERVER_DIR: ${{ inputs.working-directory == '.' && inputs.server-path || format('{0}/{1}', inputs.working-directory, inputs.server-path) }}

jobs:
prepare-analysis:
name: Prepare Sonar inputs
if: ${{ inputs.sonar-inputs-artifact == '' }}
runs-on: [appdev-selfhosted-al2023]
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ inputs.branch }}
- name: Set up project JDK
uses: actions/setup-java@v4
with:
java-version: ${{ inputs.project_java_version }}
distribution: temurin
- name: Configure Node
if: ${{ !inputs.REGISTRY_URL }}
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node_version }}
- name: Configure Node if using JFrog npm packages
if: ${{ inputs.REGISTRY_URL }}
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node_version }}
registry-url: ${{ inputs.REGISTRY_URL }}
scope: ${{ inputs.SCOPE }}
- name: Restore gradle.properties and setup
working-directory: ${{ inputs.working-directory }}
shell: bash
env:
GRADLE_PROPERTIES: ${{ secrets.GRADLE_PROPERTIES }}
run: |
set -euo pipefail
mkdir -p "${HOME}/.gradle"
echo "GRADLE_USER_HOME=${HOME}/.gradle" >> "${GITHUB_ENV}"
{
echo "genesisArtifactoryUser=${{ secrets.JFROG_USERNAME }}"
echo "genesisArtifactoryPassword=${{ secrets.JFROG_PASSWORD }}"
echo "systemProp.org.gradle.internal.http.connectionTimeout=180000"
echo "systemProp.org.gradle.internal.http.socketTimeout=180000"
echo "dockerUrl=genesisglobal-docker-internal.jfrog.io"
echo "dockerUsername=${{ secrets.JFROG_USERNAME }}"
echo "dockerPassword=${{ secrets.JFROG_PASSWORD }}"
echo "dockerEmail=${{ secrets.JFROG_EMAIL }}"
echo "genesis-home=../.genesis-home"
echo "deploy-plugin-mode=local"
} > "${HOME}/.gradle/gradle.properties"
if [ -n "${GRADLE_PROPERTIES:-}" ]; then
printf '%s\n' "${GRADLE_PROPERTIES}" >> "${HOME}/.gradle/gradle.properties"
fi
chmod +x ./gradlew
chmod +x "./${{ inputs.server-path }}/gradlew"
chmod -R +rx "./${{ inputs.server-path }}"
cat "${HOME}/.gradle/gradle.properties" >> ./gradle.properties
RELEASE_VERSION="$(echo '${{ inputs.version }}' | sed 's/\//-/g')"
echo "RELEASE_VERSION=${RELEASE_VERSION}" >> "${GITHUB_ENV}"
BUILD_FILE="${{ inputs.server-path }}/build.gradle.kts"
if [ -n "${RELEASE_VERSION}" ] && [ -f "${BUILD_FILE}" ]; then
sed -E -i "s/^[[:space:]]{4}version = \".+\"/ version = \"${RELEASE_VERSION}\"/g" "${BUILD_FILE}"
fi
Comment on lines +96 to +129

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 **Critical - Shell Command/Script Injection Vulnerability**

Directly interpolating secrets and inputs (such as ${{ secrets.JFROG_USERNAME }}, ${{ secrets.JFROG_PASSWORD }}, ${{ inputs.version }}, and ${{ inputs.server-path }}) into inline bash shell scripts is a serious security risk. If any of these values contain special characters (e.g. single/double quotes, spaces, semicolons, etc.), it could result in command/script injection, shell syntax errors, or accidental exposure in execution logs.

Remediation:
Always map workflow inputs and secrets to environment variables within the step's env: context block, and reference them using standard shell environment variable syntax ($VARIABLE_NAME). This ensures they are safely handled by the shell runner as values rather than raw script commands.

Suggested change
- name: Restore gradle.properties and setup
working-directory: ${{ inputs.working-directory }}
shell: bash
env:
GRADLE_PROPERTIES: ${{ secrets.GRADLE_PROPERTIES }}
run: |
set -euo pipefail
mkdir -p "${HOME}/.gradle"
echo "GRADLE_USER_HOME=${HOME}/.gradle" >> "${GITHUB_ENV}"
{
echo "genesisArtifactoryUser=${{ secrets.JFROG_USERNAME }}"
echo "genesisArtifactoryPassword=${{ secrets.JFROG_PASSWORD }}"
echo "systemProp.org.gradle.internal.http.connectionTimeout=180000"
echo "systemProp.org.gradle.internal.http.socketTimeout=180000"
echo "dockerUrl=genesisglobal-docker-internal.jfrog.io"
echo "dockerUsername=${{ secrets.JFROG_USERNAME }}"
echo "dockerPassword=${{ secrets.JFROG_PASSWORD }}"
echo "dockerEmail=${{ secrets.JFROG_EMAIL }}"
echo "genesis-home=../.genesis-home"
echo "deploy-plugin-mode=local"
} > "${HOME}/.gradle/gradle.properties"
if [ -n "${GRADLE_PROPERTIES:-}" ]; then
printf '%s\n' "${GRADLE_PROPERTIES}" >> "${HOME}/.gradle/gradle.properties"
fi
chmod +x ./gradlew
chmod +x "./${{ inputs.server-path }}/gradlew"
chmod -R +rx "./${{ inputs.server-path }}"
cat "${HOME}/.gradle/gradle.properties" >> ./gradle.properties
RELEASE_VERSION="$(echo '${{ inputs.version }}' | sed 's/\//-/g')"
echo "RELEASE_VERSION=${RELEASE_VERSION}" >> "${GITHUB_ENV}"
BUILD_FILE="${{ inputs.server-path }}/build.gradle.kts"
if [ -n "${RELEASE_VERSION}" ] && [ -f "${BUILD_FILE}" ]; then
sed -E -i "s/^[[:space:]]{4}version = \".+\"/ version = \"${RELEASE_VERSION}\"/g" "${BUILD_FILE}"
fi
- name: Restore gradle.properties and setup
working-directory: ${{ inputs.working-directory }}
shell: bash
env:
GRADLE_PROPERTIES: ${{ secrets.GRADLE_PROPERTIES }}
INPUT_VERSION: ${{ inputs.version }}
INPUT_SERVER_PATH: ${{ inputs.server-path }}
JFROG_USERNAME: ${{ secrets.JFROG_USERNAME }}
JFROG_PASSWORD: ${{ secrets.JFROG_PASSWORD }}
JFROG_EMAIL: ${{ secrets.JFROG_EMAIL }}
run: |
set -euo pipefail
mkdir -p "${HOME}/.gradle"
echo "GRADLE_USER_HOME=${HOME}/.gradle" >> "${GITHUB_ENV}"
{
echo "genesisArtifactoryUser=${JFROG_USERNAME}"
echo "genesisArtifactoryPassword=${JFROG_PASSWORD}"
echo "systemProp.org.gradle.internal.http.connectionTimeout=180000"
echo "systemProp.org.gradle.internal.http.socketTimeout=180000"
echo "dockerUrl=genesisglobal-docker-internal.jfrog.io"
echo "dockerUsername=${JFROG_USERNAME}"
echo "dockerPassword=${JFROG_PASSWORD}"
echo "dockerEmail=${JFROG_EMAIL}"
echo "genesis-home=../.genesis-home"
echo "deploy-plugin-mode=local"
} > "${HOME}/.gradle/gradle.properties"
if [ -n "${GRADLE_PROPERTIES:-}" ]; then
printf '%s\n' "${GRADLE_PROPERTIES}" >> "${HOME}/.gradle/gradle.properties"
fi
chmod +x ./gradlew
chmod +x "./${INPUT_SERVER_PATH}/gradlew"
chmod -R +rx "./${INPUT_SERVER_PATH}"
cat "${HOME}/.gradle/gradle.properties" >> ./gradle.properties
RELEASE_VERSION="$(echo "${INPUT_VERSION}" | sed 's/\//-/g')"
echo "RELEASE_VERSION=${RELEASE_VERSION}" >> "${GITHUB_ENV}"
BUILD_FILE="${INPUT_SERVER_PATH}/build.gradle.kts"
if [ -n "${RELEASE_VERSION}" ] && [ -f "${BUILD_FILE}" ]; then
sed -E -i "s/^[[:space:]]{4}version = \".+\"/ version = \"${RELEASE_VERSION}\"/g" "${BUILD_FILE}"
fi

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
with:
cache-disabled: true
- name: Run tests and generate coverage
working-directory: ${{ env.SERVER_DIR }}
shell: bash
run: |
set -euo pipefail
./gradlew test jacocoTestReport --no-configuration-cache --stacktrace
- name: Upload Sonar inputs
uses: actions/upload-artifact@v4
with:
name: ${{ env.SONAR_ARTIFACT_NAME }}
retention-days: 1
if-no-files-found: error
Comment on lines +140 to +145

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 **Medium - Reusable Workflow Robustness**

Using if-no-files-found: error in shared reusable workflows can cause premature build failures for caller repositories that do not produce all listed paths (such as test-results, jacoco, or classes folders). Setting this to warn prevents builds from failing while still alerting developers via the action logs if files are missing.

Remediation:
Change if-no-files-found to warn to make the artifact upload robust and tolerant to missing build outputs.

Suggested change
- name: Upload Sonar inputs
uses: actions/upload-artifact@v4
with:
name: ${{ env.SONAR_ARTIFACT_NAME }}
retention-days: 1
if-no-files-found: error
- name: Upload Sonar inputs
uses: actions/upload-artifact@v4
with:
name: ${{ env.SONAR_ARTIFACT_NAME }}
retention-days: 1
if-no-files-found: warn

path: |
${{ env.SERVER_DIR }}/**/build/classes/**
${{ env.SERVER_DIR }}/**/build/generated/**
${{ env.SERVER_DIR }}/**/build/reports/**
${{ env.SERVER_DIR }}/**/build/test-results/**
${{ env.SERVER_DIR }}/**/build/jacoco/**
${{ env.SERVER_DIR }}/build/**/reports/**
${{ env.SERVER_DIR }}/build/**/jacoco/**

sonar-analysis:
name: SonarCloud analysis
needs: prepare-analysis
# Prepare is skipped when the caller already uploaded Sonar inputs; still run analysis.
if: ${{ !cancelled() && (needs.prepare-analysis.result == 'success' || (needs.prepare-analysis.result == 'skipped' && inputs.sonar-inputs-artifact != '')) }}
runs-on: [appdev-selfhosted-al2023]
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ inputs.branch }}
# Project toolchain JDK must be present even with skipCompile — Gradle still
# configures compileJava and resolves dependencies against languageVersion.
- name: Set up project JDK
uses: actions/setup-java@v4
with:
java-version: ${{ inputs.project_java_version }}
distribution: temurin
- name: Set up JDK 21 for Sonar
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: temurin
- name: Configure Node
if: ${{ !inputs.REGISTRY_URL }}
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node_version }}
- name: Configure Node if using JFrog npm packages
if: ${{ inputs.REGISTRY_URL }}
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node_version }}
registry-url: ${{ inputs.REGISTRY_URL }}
scope: ${{ inputs.SCOPE }}
- name: Restore gradle.properties and setup
working-directory: ${{ inputs.working-directory }}
shell: bash
env:
GRADLE_PROPERTIES: ${{ secrets.GRADLE_PROPERTIES }}
run: |
set -euo pipefail
mkdir -p "${HOME}/.gradle"
echo "GRADLE_USER_HOME=${HOME}/.gradle" >> "${GITHUB_ENV}"
{
echo "genesisArtifactoryUser=${{ secrets.JFROG_USERNAME }}"
echo "genesisArtifactoryPassword=${{ secrets.JFROG_PASSWORD }}"
echo "systemProp.org.gradle.internal.http.connectionTimeout=180000"
echo "systemProp.org.gradle.internal.http.socketTimeout=180000"
echo "dockerUrl=genesisglobal-docker-internal.jfrog.io"
echo "dockerUsername=${{ secrets.JFROG_USERNAME }}"
echo "dockerPassword=${{ secrets.JFROG_PASSWORD }}"
echo "dockerEmail=${{ secrets.JFROG_EMAIL }}"
echo "genesis-home=../.genesis-home"
echo "deploy-plugin-mode=local"
} > "${HOME}/.gradle/gradle.properties"
if [ -n "${GRADLE_PROPERTIES:-}" ]; then
printf '%s\n' "${GRADLE_PROPERTIES}" >> "${HOME}/.gradle/gradle.properties"
fi
if [ -f ./gradlew ]; then
chmod +x ./gradlew
fi
if [ -f "./${{ inputs.server-path }}/gradlew" ]; then
chmod +x "./${{ inputs.server-path }}/gradlew"
fi
if [ -d "./${{ inputs.server-path }}" ]; then
chmod -R +rx "./${{ inputs.server-path }}"
fi
cat "${HOME}/.gradle/gradle.properties" >> ./gradle.properties
RELEASE_VERSION="$(echo '${{ inputs.version }}' | sed 's/\//-/g')"
BUILD_FILE="${{ inputs.server-path }}/build.gradle.kts"
if [ -n "${RELEASE_VERSION}" ] && [ -f "${BUILD_FILE}" ]; then
sed -E -i "s/^[[:space:]]{4}version = \".+\"/ version = \"${RELEASE_VERSION}\"/g" "${BUILD_FILE}"
fi
Comment on lines +191 to +229

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 **Critical - Shell Command/Script Injection Vulnerability**

Just like in the prepare-analysis job, directly interpolating secrets and inputs (such as ${{ secrets.JFROG_USERNAME }}, ${{ secrets.JFROG_PASSWORD }}, ${{ inputs.version }}, and ${{ inputs.server-path }}) into inline bash shell scripts is a serious security risk.

Remediation:
Map the inputs and secrets to environment variables in the step's env: context block, and reference them using shell environment variables.

Suggested change
- name: Restore gradle.properties and setup
working-directory: ${{ inputs.working-directory }}
shell: bash
env:
GRADLE_PROPERTIES: ${{ secrets.GRADLE_PROPERTIES }}
run: |
set -euo pipefail
mkdir -p "${HOME}/.gradle"
echo "GRADLE_USER_HOME=${HOME}/.gradle" >> "${GITHUB_ENV}"
{
echo "genesisArtifactoryUser=${{ secrets.JFROG_USERNAME }}"
echo "genesisArtifactoryPassword=${{ secrets.JFROG_PASSWORD }}"
echo "systemProp.org.gradle.internal.http.connectionTimeout=180000"
echo "systemProp.org.gradle.internal.http.socketTimeout=180000"
echo "dockerUrl=genesisglobal-docker-internal.jfrog.io"
echo "dockerUsername=${{ secrets.JFROG_USERNAME }}"
echo "dockerPassword=${{ secrets.JFROG_PASSWORD }}"
echo "dockerEmail=${{ secrets.JFROG_EMAIL }}"
echo "genesis-home=../.genesis-home"
echo "deploy-plugin-mode=local"
} > "${HOME}/.gradle/gradle.properties"
if [ -n "${GRADLE_PROPERTIES:-}" ]; then
printf '%s\n' "${GRADLE_PROPERTIES}" >> "${HOME}/.gradle/gradle.properties"
fi
if [ -f ./gradlew ]; then
chmod +x ./gradlew
fi
if [ -f "./${{ inputs.server-path }}/gradlew" ]; then
chmod +x "./${{ inputs.server-path }}/gradlew"
fi
if [ -d "./${{ inputs.server-path }}" ]; then
chmod -R +rx "./${{ inputs.server-path }}"
fi
cat "${HOME}/.gradle/gradle.properties" >> ./gradle.properties
RELEASE_VERSION="$(echo '${{ inputs.version }}' | sed 's/\//-/g')"
BUILD_FILE="${{ inputs.server-path }}/build.gradle.kts"
if [ -n "${RELEASE_VERSION}" ] && [ -f "${BUILD_FILE}" ]; then
sed -E -i "s/^[[:space:]]{4}version = \".+\"/ version = \"${RELEASE_VERSION}\"/g" "${BUILD_FILE}"
fi
- name: Restore gradle.properties and setup
working-directory: ${{ inputs.working-directory }}
shell: bash
env:
GRADLE_PROPERTIES: ${{ secrets.GRADLE_PROPERTIES }}
INPUT_VERSION: ${{ inputs.version }}
INPUT_SERVER_PATH: ${{ inputs.server-path }}
JFROG_USERNAME: ${{ secrets.JFROG_USERNAME }}
JFROG_PASSWORD: ${{ secrets.JFROG_PASSWORD }}
JFROG_EMAIL: ${{ secrets.JFROG_EMAIL }}
run: |
set -euo pipefail
mkdir -p "${HOME}/.gradle"
echo "GRADLE_USER_HOME=${HOME}/.gradle" >> "${GITHUB_ENV}"
{
echo "genesisArtifactoryUser=${JFROG_USERNAME}"
echo "genesisArtifactoryPassword=${JFROG_PASSWORD}"
echo "systemProp.org.gradle.internal.http.connectionTimeout=180000"
echo "systemProp.org.gradle.internal.http.socketTimeout=180000"
echo "dockerUrl=genesisglobal-docker-internal.jfrog.io"
echo "dockerUsername=${JFROG_USERNAME}"
echo "dockerPassword=${JFROG_PASSWORD}"
echo "dockerEmail=${JFROG_EMAIL}"
echo "genesis-home=../.genesis-home"
echo "deploy-plugin-mode=local"
} > "${HOME}/.gradle/gradle.properties"
if [ -n "${GRADLE_PROPERTIES:-}" ]; then
printf '%s\n' "${GRADLE_PROPERTIES}" >> "${HOME}/.gradle/gradle.properties"
fi
if [ -f ./gradlew ]; then
chmod +x ./gradlew
fi
if [ -f "./${INPUT_SERVER_PATH}/gradlew" ]; then
chmod +x "./${INPUT_SERVER_PATH}/gradlew"
fi
if [ -d "./${INPUT_SERVER_PATH}" ]; then
chmod -R +rx "./${INPUT_SERVER_PATH}"
fi
cat "${HOME}/.gradle/gradle.properties" >> ./gradle.properties
RELEASE_VERSION="$(echo "${INPUT_VERSION}" | sed 's/\//-/g')"
BUILD_FILE="${INPUT_SERVER_PATH}/build.gradle.kts"
if [ -n "${RELEASE_VERSION}" ] && [ -f "${BUILD_FILE}" ]; then
sed -E -i "s/^[[:space:]]{4}version = \".+\"/ version = \"${RELEASE_VERSION}\"/g" "${BUILD_FILE}"
fi

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
with:
cache-disabled: true
- name: Download Sonar inputs
uses: actions/download-artifact@v4
with:
name: ${{ env.SONAR_ARTIFACT_NAME }}
path: ${{ github.workspace }}
- name: Run SonarCloud analysis
working-directory: ${{ env.SERVER_DIR }}
shell: bash
env:
SONAR_TOKEN: ${{ secrets.JENKINSGENESIS_SONAR }}
GITHUB_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
./gradlew sonar --no-configuration-cache --stacktrace \
-Dsonar.gradle.skipCompile=true \
-Dsonar.token="${SONAR_TOKEN}"
Loading