Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ Please install conda if you don't have it already. You can install [miniforge](h
# create the conda environment (assuming in base `cuopt` directory)
# note: cuOpt currently doesn't support `channel_priority: strict`;
# use `channel_priority: flexible` instead
conda env create --name cuopt_dev --file conda/environments/all_cuda-130_arch-$(uname -m).yaml
conda env create --name cuopt_dev --file conda/environments/all_cuda-131_arch-$(uname -m).yaml
# activate the environment
conda activate cuopt_dev
```
Expand Down
10 changes: 10 additions & 0 deletions sonar-project.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

# SonarQube Project Configuration
sonar.projectKey=GPUSW_cuOpt_Nvidia-cuOpt_cuopt
sonar.projectName=NVIDIA cuOpt
sonar.projectVersion=1.0

# Source code location
sonar.sources=.
60 changes: 60 additions & 0 deletions sonarqube/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# SonarQube Analysis

This directory contains the configuration and scripts for running automated SonarQube analysis on cuOpt branches.

## Files

- `sonar-branches.txt` - List of branches to analyze (one per line)
- `run-sonar-analysis.sh` - Automated script that clones, builds, and analyzes branches

## Quick Start

### 1. Configure Branches

Edit `sonar-branches.txt` to specify which branches to analyze:

```bash
# One branch per line
main
release/26.02

# Lines starting with # are comments
# Empty lines are ignored
```

### 2. Set Required Environment Variable

The script requires authentication:

```bash
export SONAR_TOKEN="your_token_here"
```

**Note**: Contact the cuOpt team for token details.

### 3. Run the Analysis

```bash
cd /path/to/cuopt
./sonarqube/run-sonar-analysis.sh
```

## Script Behavior

The script will automatically:

1. ✅ Validate branch configuration file exists and has at least one branch
2. ✅ Clone each branch into a fresh temporary directory
3. ✅ Create an isolated conda environment per branch
4. ✅ Build the project using `./build.sh`
5. ✅ Run SonarQube analysis with branch-specific tagging
6. ✅ Clean up temporary files and conda environments
7. ✅ Provide a summary of successful and failed branches

## Support

**Contact**: cuOpt team

For issues with:
- Build failures: See [CONTRIBUTING.md](../CONTRIBUTING.md)
- Script bugs: Report to the cuOpt team
219 changes: 219 additions & 0 deletions sonarqube/run-sonar-analysis.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
#!/bin/bash
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BRANCHES_FILE="$SCRIPT_DIR/sonar-branches.txt"
WORK_DIR="/tmp/sonar-analysis-$(date +%Y%m%d-%H%M%S)"

# Conda environment file to use for building
# Adjust this path based on your CUDA version and architecture
# Available: all_cuda-129_arch-{x86_64,aarch64}.yaml, all_cuda-131_arch-{x86_64,aarch64}.yaml
ARCH=$(uname -m)
CONDA_ENV_FILE="conda/environments/all_cuda-131_arch-${ARCH}.yaml"

# SonarQube Configuration
# The token should be set via environment variable SONAR_TOKEN for security
# You can also set SONAR_HOST_URL if using a custom SonarQube server
# Default: https://sonarcloud.io (if not set)
if [ -z "$SONAR_TOKEN" ]; then
echo "ERROR: SONAR_TOKEN environment variable is not set"
echo "Please set it with: export SONAR_TOKEN=your_sonarqube_token"
echo ""
echo "To generate a token:"
echo " 1. Log in to SonarQube/SonarCloud"
echo " 2. Go to My Account > Security"
echo " 3. Generate a new token"
exit 1
fi

# Optional: Set SonarQube host URL (defaults to SonarCloud if not set)
SONAR_HOST_URL="${SONAR_HOST_URL:-https://sonarcloud.io}"

echo "SonarQube Host: $SONAR_HOST_URL"

# Get git remote URL from current directory
REPO_URL=$(git config --get remote.origin.url 2>/dev/null)
if [ -z "$REPO_URL" ]; then
echo "ERROR: Could not determine git remote URL"
echo "Make sure you run this script from within the git repository"
exit 1
fi

echo "Repository URL: $REPO_URL"
echo "Working directory: $WORK_DIR"

# Create working directory
mkdir -p "$WORK_DIR"

# Cleanup function
cleanup() {
echo ""
echo "Cleaning up working directory: $WORK_DIR"
rm -rf "$WORK_DIR"
}

# Register cleanup on exit
trap cleanup EXIT
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# Check if branches file exists
if [ ! -f "$BRANCHES_FILE" ]; then
echo "ERROR: Branches file not found: $BRANCHES_FILE"
exit 1
fi

# Read and validate branches
branches=()
while IFS= read -r branch || [ -n "$branch" ]; do
# Skip comments and empty lines
[[ "$branch" =~ ^#.*$ ]] && continue
[[ -z "${branch// }" ]] && continue

# Trim whitespace and add to array
branch=$(echo "$branch" | xargs)
branches+=("$branch")
done < "$BRANCHES_FILE"

# Fail if no branches found
if [ ${#branches[@]} -eq 0 ]; then
echo "ERROR: No branches configured in $BRANCHES_FILE"
echo "Please add at least one branch to the file."
exit 1
fi

echo "Found ${#branches[@]} branch(es) to process: ${branches[*]}"
echo "Host: $(hostname)"
echo "Start time: $(date)"

# Track success/failure
successful_branches=()
failed_branches=()

# Process each branch
for branch in "${branches[@]}"; do
echo "=========================================="
echo "Processing branch: $branch"
echo "=========================================="

# Create a safe directory name from branch name
safe_branch_name="${branch//\//_}"
clone_dir="$WORK_DIR/$safe_branch_name"

# Clone the specific branch
echo "Cloning branch: $branch into $clone_dir"
if ! git clone --single-branch --branch "$branch" --depth 1 "$REPO_URL" "$clone_dir" 2>&1 | tee /tmp/clone_${safe_branch_name}.log; then
echo "ERROR: Failed to clone branch: $branch"
failed_branches+=("$branch (clone failed)")
continue
fi

# Change to cloned directory
cd "$clone_dir"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

# Setup conda environment, build, and analyze
echo "Setting up conda environment for: $branch"

# Create a unique conda environment name for this branch
conda_env_name="cuopt_sonar_${safe_branch_name}"

# Create conda environment
if ! conda env create -n "$conda_env_name" -f "$CONDA_ENV_FILE" 2>&1 | tee /tmp/conda_create_${safe_branch_name}.log; then
echo "ERROR: Conda environment creation failed for branch: $branch. Check logs at /tmp/conda_create_${safe_branch_name}.log"
failed_branches+=("$branch (conda env creation failed)")
rm -rf "$clone_dir"
continue
fi
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# Activate conda environment and run build + analysis in a subshell
echo "Building and analyzing branch: $branch in conda environment: $conda_env_name"

if ! bash -c "
set -e
source \$(conda info --base)/etc/profile.d/conda.sh
conda activate $conda_env_name

echo 'Conda environment activated: $conda_env_name'
echo 'Python version:' \$(python --version)

# Build the project
echo 'Building project...'
if ! ./build.sh 2>&1 | tee /tmp/build_${safe_branch_name}.log; then
echo 'Build failed'
exit 1
fi

# Run SonarQube analysis
echo 'Running SonarQube analysis...'
if ! sonar-scanner \
-Dsonar.host.url='$SONAR_HOST_URL' \
-Dsonar.token='$SONAR_TOKEN' \
-Dsonar.branch.name='$branch' \
2>&1 | tee /tmp/sonar_${safe_branch_name}.log; then
echo 'SonarQube analysis failed'
exit 1
fi

echo 'Build and analysis completed successfully'
"; then
Comment on lines +145 to +173
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd sonarqube && ls -la

Repository: NVIDIA/cuopt

Length of output: 370


🏁 Script executed:

cat -n run-sonar-analysis.sh | head -200

Repository: NVIDIA/cuopt

Length of output: 109


🏁 Script executed:

# Check if sonar-branches.txt exists
find . -name "sonar-branches.txt" -o -name "*branches*" 2>/dev/null

Repository: NVIDIA/cuopt

Length of output: 86


🏁 Script executed:

cat -n sonarqube/run-sonar-analysis.sh | sed -n '140,180p'

Repository: NVIDIA/cuopt

Length of output: 1648


🏁 Script executed:

cat sonarqube/sonar-branches.txt

Repository: NVIDIA/cuopt

Length of output: 324


🏁 Script executed:

# Check how branches are read and processed in the script
cat -n sonarqube/run-sonar-analysis.sh | head -100

Repository: NVIDIA/cuopt

Length of output: 3731


🏁 Script executed:

# Check how safe_branch_name is derived and if there's any sanitization
cat -n sonarqube/run-sonar-analysis.sh | sed -n '100,145p'

Repository: NVIDIA/cuopt

Length of output: 2134


🏁 Script executed:

# Check conda activation and environment variable handling
cat -n sonarqube/run-sonar-analysis.sh | sed -n '1,40p'

Repository: NVIDIA/cuopt

Length of output: 1730


Prevent command injection via unquoted variable interpolation in the bash -c block.

The current code interpolates $branch and $conda_env_name into a bash -c string without proper quoting. If a branch name contains special characters or quotes, it can break out of the command context and execute arbitrary code. The safe_branch_name substitution only replaces / with _; it does not prevent quote injection.

Additionally, branch names read from sonar-branches.txt are not validated. Consider validating branch names against git check-ref-format --branch to ensure only valid Git references are accepted.

The proposed subshell approach with proper variable quoting is the correct solution:

🔧 Safer subshell-based rewrite
-  if ! bash -c "
-    set -e
-    source \$(conda info --base)/etc/profile.d/conda.sh
-    conda activate $conda_env_name
+  if ! (
+    set -e
+    source "$(conda info --base)/etc/profile.d/conda.sh"
+    conda activate "$conda_env_name"
 
-    echo 'Conda environment activated: $conda_env_name'
-    echo 'Python version:' \$(python --version)
+    echo "Conda environment activated: $conda_env_name"
+    echo "Python version: $(python --version 2>&1)"
 
     # Build the project
     echo 'Building project...'
-    ./build.sh 2>&1 | tee '$LOG_DIR/build_${safe_branch_name}.log'
+    ./build.sh 2>&1 | tee "$LOG_DIR/build_${safe_branch_name}.log"
     if [ \${PIPESTATUS[0]} -ne 0 ]; then
       echo 'Build failed'
       exit 1
     fi
 
     # Run SonarQube analysis
     # Note: SONAR_TOKEN is read from environment automatically by sonar-scanner
     echo 'Running SonarQube analysis...'
     sonar-scanner \
-      -Dsonar.branch.name='$branch' \
-      2>&1 | tee '$LOG_DIR/sonar_${safe_branch_name}.log'
+      -Dsonar.branch.name="$branch" \
+      2>&1 | tee "$LOG_DIR/sonar_${safe_branch_name}.log"
     if [ \${PIPESTATUS[0]} -ne 0 ]; then
       echo 'SonarQube analysis failed'
       exit 1
     fi
 
     echo 'Build and analysis completed successfully'
-  "; then
+  ); then

Also add branch name validation in the loop (lines 73–82) using git check-ref-format --branch "$branch" to reject invalid Git references early.

🤖 Prompt for AI Agents
In `@sonarqube/run-sonar-analysis.sh` around lines 145 - 173, The bash -c block
currently interpolates unquoted variables ($conda_env_name, $branch,
safe_branch_name) and can be exploited via quote/command injection; replace the
inline bash -c string with a safe subshell or heredoc that passes variables via
exported environment variables (or uses a single-quoted heredoc) so no
user-controlled value is injected into the command string, ensure all usages of
safe_branch_name and LOG_DIR in the subshell are quoted, and run sonar-scanner
and ./build.sh with their outputs piped to tee as before; additionally, add
validation where branches are read by invoking git check-ref-format --branch
"$branch" (and reject/skip invalid branches early) to prevent invalid or
malicious branch names from entering the workflow.

echo "ERROR: Build or analysis failed for branch: $branch"
if grep -q "Build failed" /tmp/build_${safe_branch_name}.log 2>/dev/null; then
failed_branches+=("$branch (build failed)")
else
failed_branches+=("$branch (sonar analysis failed)")
fi

# Clean up conda environment
conda env remove -n "$conda_env_name" -y 2>/dev/null || true
rm -rf "$clone_dir"
continue
fi

# Clean up conda environment after successful analysis
echo "Cleaning up conda environment: $conda_env_name"
conda env remove -n "$conda_env_name" -y 2>/dev/null || true

successful_branches+=("$branch")
echo "✓ Successfully completed analysis for: $branch"
echo "Progress: ${#successful_branches[@]} succeeded, ${#failed_branches[@]} failed out of ${#branches[@]} total"

# Clean up clone directory after successful analysis
echo "Cleaning up clone directory for: $branch"
rm -rf "$clone_dir"
done

# Final summary
echo "=========================================="
echo "SonarQube Analysis Complete"
echo "=========================================="
echo "Total branches: ${#branches[@]}"
echo "Successful: ${#successful_branches[@]}"
echo "Failed: ${#failed_branches[@]}"
echo ""

if [ ${#successful_branches[@]} -gt 0 ]; then
echo "✓ Successful branches:"
for branch in "${successful_branches[@]}"; do
echo " - $branch"
done
echo ""
fi

if [ ${#failed_branches[@]} -gt 0 ]; then
echo "✗ Failed branches:"
for branch in "${failed_branches[@]}"; do
echo " - $branch"
done
echo ""
fi

echo "End time: $(date)"
echo "=========================================="

# Exit with error if any branches failed
if [ ${#failed_branches[@]} -gt 0 ]; then
echo "ERROR: ${#failed_branches[@]} branch(es) failed analysis"
exit 1
fi

echo "All branches processed successfully!"
exit 0
12 changes: 12 additions & 0 deletions sonarqube/sonar-branches.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# SonarQube Branch List
# One branch name per line
# Lines starting with # are comments and should be ignored by the cron job
# Empty lines are also ignored

# Main development branches
main
release/26.02

# Add release branches as needed
# release/v1.0
# release/v2.0
Loading