A self-hosted GitHub app that listens for pull request events, scans them for malicious code, and comments detections directly on the pull request.
Typically, security scans are run by workflow files. However, files can be modified, and when dealing with source modification attacks, should be avoided. A GitHub-app approach addresses this gap, ensuring the scan is not bypassed while providing better flexibility.
The app's logic can be leveraged to run any scan. All you need is to add a scanner method to the scan logic.
Currently, PRevent detects dynamic code execution and obfuscation, patterns found in nearly 100% of malicious code attacks reported to this day, while being rare in benign code, making the scan very effective. It uses Apiiro's malicious-code-ruleset for Semgrep, alongside additional Python-based detectors. Only rules and detectors with low false-positive rates are included. When a false-positive occurs, it's almost always due to poor coding practices.
Optional features:
- Select repositories and branches to include or exclude from the scan (default: all).
- Trigger code reviews from designated reviewers.
- Block merging until a reviewer's approval is granted or the scan passes.
- Run only the rules and detectors with the lowest false-positive rates.
Deployment:
- Supports containerization.
- Non-containerized deployment is fully automated with an interactive setup script.
- To manage GitHub key (required for any GitHub app), multiple secret managers are supported:
- AWS Secrets Manager
- Azure Key Vault
- Google Cloud Secret Manager
- HashiCorp Vault
- Local HashiCorp Vault (for development and testing)
- Bash
- Clojure
- C#
- Dart
- Go
- Java
- JavaScript
- TypeScript
- Lua
- PHP
- Python
- Ruby
- Rust
- Scala
Deploying PRevent involves three parts, typically completed in 5 minutes to an hour, depending on your setup and familiarity:
- Configure an existing secret manager or create a new one.
- Create a GitHub app within your GitHub Organization or account.
- Deploy the application to a server.
- If network access controls apply, the "hooks" IP addresses should be allowed.
Use the latest Python version (3.9.2+ supported).
Parts 1 and 2 are handled during the interactive setup process in step 3:
- Clone this repository:
git clone https://github.com/apiiro/prevent.git cd prevent
- Install dependencies by either poetry (recommended) or pip:
# poetry poetry install # Or pip python3 -m venv venv source venv/bin/activate pip install -r requirements.txt
- Go through the setup process:
python3 -m setup.setup
- Start the server:
# poetry poetry run uvicorn src.app:app --host 0.0.0.0 --port 8080 # Or pip source venv/bin/activate uvicorn src.app:app --host 0.0.0.0 --port 8080
The application communicates with GitHub via authenticated requests, which require three sensitive parameters:
- Private Key (
GITHUB_APP_PRIVATE_KEY
) - App ID (
GITHUB_APP_INTEGRATION_ID
) - Webhook Secret (
WEBHOOK_SECRET
)
To minimize security risks, these parameters should be stored in a secret manager with minimal permissions. Optionally, you may store additional sensitive details such as:
- Repositories and branches (
BRANCHES_INCLUDE
,BRANCHES_EXCLUDE
) - Accounts and teams (
SECURITY_REVIEWERS
)
The application handles all parameters exclusively through the secret manager (see supported managers below). In containerized deployments, this applies also for insensitive parameters, all are optional, for centralization and simplicity. Upon initialization, these parameters are written to src/settings.py
to avoid repeated fetching during runtime. These include:
- Block pull requests with detections (
BLOCK_PR
) - False-positives rate strictness (
FP_STRICT
) - Full scan instead of first detection only (
FULL_FINDINGS
) - JWT token time-to-live (
JWT_EXPIRY_SECONDS
) - Webhook listener's port (
WEBHOOK_PORT
) - has to be manually updated in the Dockerfile
First, set SECRET_MANAGER in your secret manager to either: aws, azure, gcloud, vault, or local.
Dedicate a section in your secret manager for this app, separated from the rest. Create an app role with minimal permissions, to access the dedicated section only. If you are not sure how, try the following instructions:
python3 setup/secret_managers/print_instructions.py SECRET_MANAGER
Permissions required to operate the role:
Permission | AWS | Azure | GCloud | Vault |
---|---|---|---|---|
read | secretsmanager:GetSecretValue | KeyVaultSecret:Get | secretmanager.secrets.get | read |
write | secretsmanager:PutSecretValue | KeyVaultSecret:Set | secretmanager.secrets.add | create, update |
scope | resource = "prevent-app/*" | secret = "prevent-app/*" | secret = "prevent-app/*" | path = "prevent-app/*" |
-
Go to https://github.com/settings/apps to create a new GitHub App.
-
Set metadata:
- Name: prevent
- Description: Detects malicious code in pull requests.
- URL: https://github.com/apiiro/PRevent.git
-
Set the webhook URL: the address where the app will listen. Endpoint:
/webhook
. Examples: -
Under the webhook URL field, set the secret field in order to process only requests originating from GitHub. You can run
python -c 'import secrets; print(secrets.token_hex(32))'
to generate one. Then, store it in your secrets manager as WEBHOOK_SECRET. -
Set required permissions:
Parent Permission Action Reason Repository Pull requests Read and Write Read PR, write comments (if enabled: trigger reviews) Repository Commit statuses Read and write Monitor scan-results by setting commits-statuses Repository Contents Read-only Get full files so AST can get built -
Set optional permissions:
Parent Permission Action Reason Repository Administration Read and write Manage branch protection -
Subscribe to the following events:
Pull request
Pull request review
-
Click "Create GitHub App". Copy the App ID and store it in your secret manager as GITHUB_APP_INTEGRATION_ID.
-
Generate a private key, store it in your secret manager as GITHUB_APP_PRIVATE_KEY, and make sure to delete the file.
- Set any desired optional parameters from below in your secret manager.
- Follow the Docker README to build, configure and register your container image.
- Follow the Helm README to package and deploy your container to your Kubernetes cluster according to best practices.
-
BLOCK_PR: To block merging until either a reviewer approves the pull request or the scan passes, set it to
True
in your secret manager. -
SECURITY_REVIEWERS: To trigger code reviews upon detections, configure it in your secret manager with a Python list of reviewer accounts or teams (e.g.,
['account1', 'account2', 'team:appsec']
). Ensure you runjson.dumps(security_reviewers)
or an equivalent method beforehand. -
INCLUDE_BRANCHES or EXCLUDE_BRANCHES: To include or exclude specific repos and branches for monitoring, set either in your secret manager with a Python dictionary. Use
{'repo1': 'all'}
to include or exclude all repo's branches, or specify a list of branches (e.g.,{'repo1': ['main', 'branch2'], 'repo2': 'all'}
). Ensure you runjson.dumps(security_reviewers)
or an equivalent method beforehand. By default, all repositories and branches are monitored. -
FP_STRICT: To minimize false-positives by running only
ERROR
severity rules and detectors (primarily a small subset of obfuscation detection), set it toTrue
in your secret manager. Typically, the false-positives rate is negligible regardless of enabling this option. -
FULL_FINDINGS: To maximize security and enable detection of all findings without stopping after the first detection, set it to
True
in your secret manager. While the false-positives rate may slightly increase, it generally remains negligible.
Here you can find architecture, sequence and code logic flow diagrams, and a configuration parameters summary.
Contributions are welcome through pull requests or issues.
- The app does not persist pull request states.
- Only files up to 1 MB are scanned.
- Files consisting of long single-line are excluded from scanning.
- Response time ranges from 200 milliseconds to 4 seconds, with an average of 1.2 seconds. Longer times occur when malicious-code-ruleset updates are fetched (2-10 seconds).
This repository is licensed under the MIT License.
For more information: https://apiiro.com/blog/guard-your-codebase-practical-steps-and-tools-to-prevent-malicious-code/