Skip to content
Closed
Show file tree
Hide file tree
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
10 changes: 6 additions & 4 deletions false-positive-check/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ This tool connects to a SonarQube server, iterates through all projects, and col
- Project names
- Number of false positive issues per project
- List of rule IDs that generated the false positive issues
- Quality gate used by each project
- Quality profiles associated with each project

## Requirements

Expand Down Expand Up @@ -65,10 +67,10 @@ python sonarqube_false_positives.py -u <SONARQUBE_URL> -t <AUTH_TOKEN>
### Sample Output

```csv
Project Name,Number of Issues,Rule IDs
My Web Application,5,java:S1234, java:S5678
Backend API,12,python:S9012, python:S3456, python:S7890
Frontend Dashboard,3,javascript:S2468
Project Name,Number of Issues,Rule IDs,Quality Gate,Quality Profiles
My Web Application,5,java:S1234, java:S5678,Sonar way,java: Sonar way
Backend API,12,python:S9012, python:S3456, python:S7890,Custom Gate,py: Custom Python Profile
Frontend Dashboard,3,javascript:S2468,Sonar way,js: Sonar way; ts: Sonar way
```

### Console Output
Expand Down
127 changes: 99 additions & 28 deletions false-positive-check/sonarqube_false_positives.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,17 @@
def get_false_positive_issues(self, project_key: str) -> List[Dict]:
"""
Get all issues marked as false positives for a specific project

Args:
project_key: The project key

Returns:
List of issue dictionaries
"""
issues = []
page = 1
page_size = 500

while True:
url = f"{self.base_url}/api/issues/search"
params = {
Expand All @@ -95,99 +95,170 @@
'p': page,
'ps': page_size
}

try:
response = self.session.get(url, params=params)
response.raise_for_status()
data = response.json()

issues.extend(data.get('issues', []))

# Check if there are more pages
paging = data.get('paging', {})
total = paging.get('total', 0)

if page * page_size >= total:
break

page += 1

except requests.exceptions.RequestException as e:
print(f"Error fetching issues for project {project_key}: {e}", file=sys.stderr)
break

return issues

def get_project_quality_gate(self, project_key: str) -> str:
"""
Get the quality gate associated with a project

Args:
project_key: The project key

Returns:
Quality gate name or 'N/A' if not found
"""
url = f"{self.base_url}/api/qualitygates/get_by_project"
params = {'project': project_key}

try:
response = self.session.get(url, params=params)
response.raise_for_status()
data = response.json()

quality_gate = data.get('qualityGate', {})
return quality_gate.get('name', 'N/A')

except requests.exceptions.RequestException as e:
print(f"Error fetching quality gate for project {project_key}: {e}", file=sys.stderr)
return 'N/A'

def get_quality_profiles_for_project(self, project_key: str) -> Dict[str, str]:
"""
Get quality profiles associated with a project

Args:
project_key: The project key

Returns:
Dictionary mapping language to quality profile name
"""
url = f"{self.base_url}/api/qualityprofiles/search"
params = {'project': project_key}

try:
response = self.session.get(url, params=params)
response.raise_for_status()
data = response.json()

profiles = {}
for profile in data.get('profiles', []):
language = profile.get('language', 'unknown')
name = profile.get('name', 'N/A')
profiles[language] = name

return profiles

except requests.exceptions.RequestException as e:
print(f"Error fetching quality profiles for project {project_key}: {e}", file=sys.stderr)
return {}


def analyze_false_positives(sonarqube_url: str, token: str = None) -> List[Dict]:
"""
Analyze false positive issues across all projects

Args:
sonarqube_url: SonarQube server URL
token: Authentication token (optional)

Returns:
List of dictionaries with project analysis results
"""
client = SonarQubeClient(sonarqube_url, token)

print("Fetching all projects...")
projects = client.get_all_projects()
print(f"Found {len(projects)} projects")

results = []

for i, project in enumerate(projects, 1):
project_key = project.get('key')
project_name = project.get('name', project_key)

print(f"Processing project {i}/{len(projects)}: {project_name}")

# Get false positive issues for this project
issues = client.get_false_positive_issues(project_key)

# Collect unique rule IDs
rule_ids: Set[str] = set()
for issue in issues:
rule = issue.get('rule')
if rule:
rule_ids.add(rule)


# Get quality gate for the project
quality_gate = client.get_project_quality_gate(project_key)

# Get quality profiles for the project
quality_profiles = client.get_quality_profiles_for_project(project_key)

results.append({
'project_name': project_name,
'project_key': project_key,
'issue_count': len(issues),
'rule_ids': sorted(list(rule_ids))
'rule_ids': sorted(list(rule_ids)),

Check warning on line 222 in false-positive-check/sonarqube_false_positives.py

View check run for this annotation

robertosonartest / SonarQube Code Analysis

Remove this redundant call.

[S7508] Redundant collection functions should be avoided See more on https://robertol.ngrok.io/project/issues?id=roberto-luzardo-sonarsource_tools-and-scripts_127d9653-6911-4429-8655-8d3754cf3ae6&pullRequest=1&issues=adc77522-7298-47e4-817e-96de62a34e28&open=adc77522-7298-47e4-817e-96de62a34e28
'quality_gate': quality_gate,
'quality_profiles': quality_profiles
})

return results


def export_to_csv(results: List[Dict], output_file: str = 'sonarqube_false_positives.csv'):
"""
Export results to CSV file

Args:
results: List of project analysis results
output_file: Output CSV file path
"""
with open(output_file, 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = ['Project Name', 'Number of Issues', 'Rule IDs']
fieldnames = ['Project Name', 'Number of Issues', 'Rule IDs', 'Quality Gate', 'Quality Profiles']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)

writer.writeheader()

for result in results:
# Convert rule IDs list to comma-separated string
rule_ids_str = ', '.join(result['rule_ids']) if result['rule_ids'] else ''


# Convert quality profiles dict to formatted string
quality_profiles_str = ''
if result.get('quality_profiles'):
profiles_list = [f"{lang}: {name}" for lang, name in result['quality_profiles'].items()]
quality_profiles_str = '; '.join(profiles_list)

writer.writerow({
'Project Name': result['project_name'],
'Number of Issues': result['issue_count'],
'Rule IDs': rule_ids_str
'Rule IDs': rule_ids_str,
'Quality Gate': result.get('quality_gate', 'N/A'),
'Quality Profiles': quality_profiles_str
})

print(f"\nReport exported to: {output_file}")


Expand Down
Loading