-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0a6abee
commit 1008d69
Showing
6 changed files
with
286 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Contributing to This Project | ||
|
||
First off, thank you for considering contributing to this project. It's people like you that make this project such a great tool. | ||
|
||
## How Can I Contribute? | ||
|
||
### Reporting Bugs | ||
|
||
- **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/austimkelly/ghas-utils/issues). | ||
- If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/austimkelly/ghas-utils/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** or an **executable test case** demonstrating the expected behavior that is not occurring. | ||
|
||
### Suggesting Enhancements | ||
|
||
If you have a suggestion that is not a bug and may make this project better, you can use the issue tracker to submit your suggestion. Include a clear title and description in your issue. | ||
|
||
### Pull Requests | ||
|
||
- Fork the repository and create your branch from `main`. | ||
- Issue that pull request! | ||
|
||
## Code of Conduct | ||
|
||
This project and everyone participating in it is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. | ||
|
||
## Thank You! | ||
|
||
Your contributions to the community are greatly appreciated. Every little bit helps and credit will always be given. |
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# This is a comment. | ||
# Each line is a file pattern followed by one or more owners. | ||
|
||
# Please note that the CODEOWNERS file needs to be in the repository's root, docs/, or .github/ directory. | ||
|
||
# These owners will be the default owners for everything in the repo. | ||
* @austimkelly |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,41 @@ | ||
# ghas-utils | ||
Utilities for getting insights from Github Advanced Security | ||
# GHAS Scan | ||
|
||
This is a Python script that interacts with the GitHub API to fetch repository details and code scanning analysis information. | ||
Make sure the repository exists and your GitHub token has the necessary permissions to access it. | ||
## Prerequisites | ||
|
||
- Python 3.6 or higher | ||
- `requests` library | ||
|
||
## Installation | ||
|
||
1. Clone this repository: | ||
```bash | ||
git clone [email protected]:austimkelly/ghas-utils.git | ||
``` | ||
2. Navigate to the cloned repository: | ||
```bash | ||
cd ghas-utils | ||
``` | ||
3. Install the required Python libraries: | ||
```bash | ||
pip3 install requests | ||
``` | ||
or | ||
|
||
```bash | ||
pip3 install -r requests.txt | ||
``` | ||
|
||
## Usage | ||
|
||
1. Open `ghas-scan.py` in your favorite text editor. | ||
2. Replace `access_token` variable value with your GitHub personal access token. | ||
3. Replace `owner_type` variabe value with `user` or `org`. | ||
4. Replace `owner_name` variable value with the corresponding user or org name. | ||
5. Run the script: | ||
```bash | ||
python3 ghas-scan.py | ||
``` | ||
|
||
Output is written to `github_data.json` at the repository root. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
import csv | ||
import requests | ||
import json | ||
|
||
# https://docs.github.com/en/rest/dependabot/alerts?apiVersion=2022-11-28#list-dependabot-alerts-for-a-repository | ||
import requests | ||
|
||
def get_dependabot_alerts(owner, repo_name, headers): | ||
dependabot_url = f'https://api.github.com/repos/{owner}/{repo_name}/dependabot/alerts' | ||
dependabot_alerts = requests.get(dependabot_url, headers=headers) | ||
|
||
# Check if Dependabot alerts are available | ||
if dependabot_alerts.status_code == 200: | ||
dependabot_alerts_data = dependabot_alerts.json() | ||
|
||
# Check if Dependabot is enabled | ||
dependabot_enabled = True if dependabot_alerts_data else False | ||
|
||
# Filter open alerts | ||
open_alerts = [alert for alert in dependabot_alerts_data if alert['state'] == 'open'] | ||
|
||
# Count the number of open alerts | ||
open_alerts_count = len(open_alerts) | ||
|
||
# Initialize severity counts | ||
num_critical_dep_alerts = 0 | ||
num_high_dep_alerts = 0 | ||
num_medium_dep_alerts = 0 | ||
num_low_dep_alerts = 0 | ||
|
||
# Categorize open alerts based on severity | ||
for alert in open_alerts: | ||
severity = alert['security_advisory']['severity'] | ||
if severity == 'critical': | ||
num_critical_dep_alerts += 1 | ||
elif severity == 'high': | ||
num_high_dep_alerts += 1 | ||
elif severity == 'medium': | ||
num_medium_dep_alerts += 1 | ||
elif severity == 'low': | ||
num_low_dep_alerts += 1 | ||
|
||
else: | ||
dependabot_enabled = False | ||
open_alerts_count = 0 | ||
num_critical_dep_alerts = 0 | ||
num_high_dep_alerts = 0 | ||
num_medium_dep_alerts = 0 | ||
num_low_dep_alerts = 0 | ||
|
||
return ( | ||
dependabot_enabled, | ||
open_alerts_count, | ||
num_critical_dep_alerts, | ||
num_high_dep_alerts, | ||
num_medium_dep_alerts, | ||
num_low_dep_alerts | ||
) | ||
|
||
def get_tool_name(owner, repo, headers): | ||
url = "https://api.github.com/repos/" + owner + "/" + repo + "/code-scanning/analyses" | ||
|
||
try: | ||
response = requests.get(url, headers=headers) | ||
if response.status_code == 404: | ||
return "None" | ||
response.raise_for_status() # Raises a HTTPError if the status is 4xx, 5xx | ||
data = response.json() | ||
|
||
# Specify the filename | ||
filename = "output.json" | ||
|
||
# Open the file in write mode ('w') | ||
with open(filename, 'w') as file: | ||
# Use json.dump to write the data to the file | ||
json.dump(data, file) | ||
|
||
if data: | ||
return data[0]['tool']['name'] | ||
else: | ||
return "No data received from the server" | ||
except requests.exceptions.HTTPError as http_err: | ||
return f"HTTPError: {http_err}" | ||
except Exception as err: | ||
return f"Error occurred: {err}" | ||
|
||
def get_codeowners(owner, repo_name, headers): | ||
codeowners_url = f'https://api.github.com/repos/{owner}/{repo_name}/community/codeowners' | ||
codeowners_response = requests.get(codeowners_url, headers=headers) | ||
if codeowners_response.status_code == 200: | ||
codeowners = codeowners_response.text | ||
else: | ||
codeowners = "Not found" | ||
return codeowners | ||
|
||
def get_repo_details(owner, repo_name): | ||
# Construct the repository URL using the owner and repo_name variables | ||
repo_url = f'https://api.github.com/repos/{owner}/{repo_name}' | ||
|
||
# Send a GET request to the repo_url and retrieve the repository information | ||
repo_info = requests.get(repo_url, headers=headers).json() | ||
|
||
# Additional API calls for more details | ||
# You may need to handle pagination for larger repositories | ||
|
||
# Get CODEOWNERS file | ||
codeowners =get_codeowners(owner, repo_name, headers) | ||
|
||
# Last commit date | ||
commits_url = f'https://api.github.com/repos/{owner}/{repo_name}/commits?per_page=1' | ||
last_commit_date = requests.get(commits_url, headers=headers).json()[0]['commit']['author']['date'] | ||
|
||
# First commit date | ||
first_commit_url = f'https://api.github.com/repos/{owner}/{repo_name}/commits?per_page=1' | ||
first_commit_date = requests.get(first_commit_url, headers=headers).json()[-1]['commit']['author']['date'] | ||
|
||
# Is a fork | ||
is_fork = repo_info['fork'] | ||
|
||
# Public or private | ||
is_private = repo_info['private'] | ||
|
||
# Is archived | ||
is_archived = repo_info['archived'] | ||
|
||
# Check if secrets scanning is enabled | ||
secrets_scanning_url = f'https://api.github.com/repos/{owner}/{repo_name}/secret-scanning/alerts' | ||
secrets_scanning_enabled = len(requests.get(secrets_scanning_url, headers=headers).json()) > 0 | ||
|
||
# Get names of code scanners | ||
# https://docs.github.com/en/rest/code-scanning/code-scanning?apiVersion=2022-11-28#list-code-scanning-analyses-for-a-repository | ||
code_scanners_enabled = get_tool_name(owner, repo_name, headers ) | ||
|
||
# Check the number of Dependabot alerts | ||
dependabot_enabled, open_alerts_count, num_critical_dep_alerts, num_high_dep_alerts, num_medium_dep_alerts, num_low_dep_alerts = get_dependabot_alerts(owner, repo_name, headers) | ||
|
||
# Other details can be fetched similarly | ||
|
||
return { | ||
'repo_name': repo_info['name'], | ||
'codeowners': codeowners, | ||
'last_commit_date': last_commit_date, | ||
'first_commit_date': first_commit_date, | ||
'is_fork': is_fork, | ||
'is_private': is_private, | ||
'is_archived': is_archived, | ||
'secrets_scanning_enabled': secrets_scanning_enabled, | ||
'code_scanners_enabled': code_scanners_enabled, | ||
'dependabot_enabled': dependabot_enabled, | ||
'dependabot_open_alerts_count': open_alerts_count, | ||
'num_critical_dep_alerts': num_critical_dep_alerts, | ||
'num_high_dep_alerts': num_high_dep_alerts, | ||
'num_medium_dep_alerts': num_medium_dep_alerts, | ||
'num_low_dep_alerts': num_low_dep_alerts | ||
# Add other details here | ||
} | ||
|
||
def get_repos(owner): | ||
if owner_type == 'user': | ||
repos_url = f'https://api.github.com/users/{owner}/repos' | ||
elif owner_type == 'org': | ||
repos_url = f'https://api.github.com/orgs/{owner}/repos' | ||
else: | ||
raise ValueError("Invalid owner type. Use 'user' or 'org'.") | ||
|
||
response = requests.get(repos_url, headers=headers) | ||
if response.status_code == 200: | ||
repos = response.json() | ||
return repos | ||
else: | ||
raise Exception(f"Failed to fetch repositories. Status code: {response.status_code}, Response: {response.text}") | ||
|
||
# Set the GitHub owner type, owner name, and personal access token | ||
owner_type = 'user' # Change to 'org' if needed | ||
owner_name = 'austimkelly' | ||
access_token = 'CHANGEME' | ||
|
||
# Set up headers with the access token | ||
headers = {'Authorization': f'token {access_token}'} | ||
|
||
# Get list of repositories for the user or organization | ||
repos = get_repos(owner_name) | ||
|
||
# Write data to CSV | ||
csv_filename = 'github_data.csv' | ||
with open(csv_filename, 'w', newline='') as csvfile: | ||
fieldnames = ['repo_name', | ||
'codeowners', | ||
'last_commit_date', | ||
'first_commit_date', | ||
'is_fork', | ||
'is_private', | ||
'is_archived', | ||
'secrets_scanning_enabled', | ||
'code_scanners_enabled', | ||
'dependabot_enabled', | ||
'dependabot_open_alerts_count', | ||
'num_critical_dep_alerts', | ||
'num_high_dep_alerts', | ||
'num_medium_dep_alerts', | ||
'num_low_dep_alerts',] | ||
|
||
# Add other fieldnames here | ||
writer = csv.DictWriter(csvfile, fieldnames=fieldnames) | ||
writer.writeheader() | ||
|
||
for repo in repos: | ||
repo_details = get_repo_details(owner_name, repo['name']) | ||
writer.writerow(repo_details) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
[{"ref": "refs/heads/main", "commit_sha": "588ef7b372c84e2c504aa2557224d98b90618756", "analysis_key": ".github/workflows/codeql.yml:analyze", "environment": "{\"language\":\"python\"}", "category": "/language:python", "error": "", "created_at": "2023-11-18T17:32:41Z", "results_count": 0, "rules_count": 36, "id": 142162467, "url": "https://api.github.com/repos/austimkelly/rekon/code-scanning/analyses/142162467", "sarif_id": "7a0572fa-8638-11ee-97f0-64f950e652f6", "tool": {"name": "CodeQL", "guid": null, "version": "2.15.2"}, "deletable": true, "warning": ""}] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
requests==2.26.0 |