Skip to content

Commit

Permalink
Adjust setup process to recent changes
Browse files Browse the repository at this point in the history
  • Loading branch information
Matan-Giladi committed Jan 12, 2025
1 parent 33de7b9 commit e08c772
Show file tree
Hide file tree
Showing 7 changed files with 45 additions and 57 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ EXPOSE 8080
ENV FLASK_ENV=production

# Run setup.py from the ./setup directory
RUN python /pr-event/setup/setup.py
CMD ["python", "-m", "setup.setup"]

# Change CMD to use Gunicorn through Poetry
CMD ["poetry", "run", "gunicorn", "--bind", "0.0.0.0:8080", "src.app:app"]
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This app addresses a common security gap in workflow-based malware scans, where

Currently, PR-event detects dynamic code execution and obfuscation, patterns found in nearly 100% of malware-in-code attacks reported to this day. It uses Apiiro's [malicious-code-ruleset](https://github.com/apiiro/malicious-code-ruleset.git) for Semgrep, alongside additional Python-based detectors. Only rules and detectors with low false-positive rates are included.

To run only the rules with the best impact to FP ratio, set `FP_STRICT` to `True` in `settings.py`. This will run only detectors and rules with severity set to `ERROR`.
To run only the rules with the best impact to FP ratio, set `FP_STRICT` to `True` in `src/settings.py`. This will run only detectors and rules with severity set to `ERROR`.

## Installation & Setup

Expand All @@ -28,7 +28,7 @@ PR-event can be deployed on any server to support GitHub repositories, including
```
3. Go through the setup process:
```bash
python3 setup/setup_tool.py
python3 -m setup.setup
```
4. Start the server:
```bash
Expand Down
16 changes: 8 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
[build-system]
requires = [ "poetry-core=1.0.0",]
build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "PR-event"
version = "1.0.0"
description = "An open-source GitHub app for malware-in-code scanning"
authors = ["Matan Giladi <matan.giladi@apiiro.com>"]
authors = [ "Matan Giladi <matan.giladi@apiiro.com>",]
license = "MIT"
repository = "https://github.com/apiiro/pr-event.git"
readme = "README.md"
Expand All @@ -14,17 +18,13 @@ cryptography = "44.0.0"
flask = "3.1.0"
gunicorn = "23.0.0"
pygithub = "2.5.0"
pyjwt = "2.10.1"
pygments = "2.19.0"
pyjwt = "2.10.1"
pytest = "8.3.4"
requests = "2.32.3"
semgrep = "1.102.0"
toml = "0.10.2"
werkzeug = "3.1.3"
hvac = "2.3.0"
pytest = "8.3.4"

[tool.poetry.dev-dependencies]
pytest = "8.3.4"

[build-system]
requires = ["poetry-core=1.0.0"]
build-backend = "poetry.core.masonry.api"
32 changes: 15 additions & 17 deletions setup/app_setup.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import os
import sys
import json
import logging
import secrets
from getpass import getpass
from contextlib import redirect_stderr
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
from flask import current_app
from src.secret_manager import get_secret, set_secret
from src.settings import WEBHOOK_PORT
from src.config import rewrite_setting
Expand All @@ -28,9 +27,9 @@ def is_secret_set(secret):
get_secret(secret)
return True
except KeyError:
current_app.logger.error(f"Secret '{secret}' not found.")
logging.error(f"Secret '{secret}' not found.")
except Exception as e:
current_app.logger.error(f"Error checking secret '{secret}': {e}")
logging.error(f"Error checking secret '{secret}': {e}")
return False


Expand All @@ -48,17 +47,16 @@ def set_github_app(secret_manager):
print("\n")
print("Webhook URL:")
print(" The URL where the app listens for PR events from GitHub.")
print(f" Locally, the current port is {WEBHOOK_PORT} (configured in './settings.py').")
print(f" Locally, the current port is {WEBHOOK_PORT} (set in './src/settings.py').")
port = input(
"\033[1m To change the port, enter a new value. Otherwise, press Enter: \033[0m"
) or WEBHOOK_PORT
if port != WEBHOOK_PORT:
rewrite_setting('WEBHOOK_PORT', port)
print(f"\n Successfully updated 'WEBHOOK_PORT' to {port} in 'settings.py'.")
host = get_host()
print(f" Estimated webhook URL: https://{host}:{port}/webhook")
print(" Dynamically verify you are using the correct URL")
print("\033[1m Ensure the correct URL is set in GitHub.\033[0m")
print(f"\n Local webhook URL: https://{host}:{port}/webhook")
print("\033[1m Ensure the correct URL is set in GitHub, after verifying it's accessible.\033[0m")
print(" Make sure to include the '/webhook' endpoint.")
input("\nPress Enter when you've completed this step.")

Expand All @@ -70,7 +68,7 @@ def set_webhook_secret() -> None:
random_secret = secrets.token_hex(32)
print(f"\nAuto-generated secret: {random_secret}")
webhook_secret = getpass(
"\033[1mInsert your secret (hidden), "
"\033[1mInsert your secret both here (hidden) and in GitHub, "
"or copy this secret into GitHub and press Enter: \033[0m"
) or random_secret
if not webhook_secret:
Expand Down Expand Up @@ -111,8 +109,8 @@ def set_webhook_secret() -> None:
5. If you want to block on detection:
\033[1mRepository Permissions: Administration -> Read and write\033[0m
""")
print("\nNotice that to change the permissions later,")
print("you will have to both request them in the app and approve in your account.")
print("Notice that to change the permissions later,")
print("you will have to approve in your account settings after assigning them to the app.")
input("\nPress Enter when you've completed this step.")

# App events subscriptions
Expand Down Expand Up @@ -153,7 +151,7 @@ def set_private_key() -> None:
print("(verify inputs for typos, white-spaces and correct format)")
print("Click 'Generate a private key' in GitHub, save the file.")
pk_path = input(
"\033[1mInsert the private key's file full path"
"\033[1mInsert the private key's file full path "
"to save its content in your secret manager: \033[0m"
) or ""
if not pk_path:
Expand Down Expand Up @@ -184,7 +182,7 @@ def set_private_key() -> None:
sys.exit(1)
set_secret(_private_key, private_key)
print(f"Successfully saved '{_private_key}' in your {secret_manager} secret manager.")
print("\n! DELETE THE PRIVATE KEY FILE !\n")
print("\n\033[1m! DELETE THE PRIVATE KEY FILE !\033[0m\n")
print("In GitHub app setup, you can configure the IP allow list if relevant.")

print("\n")
Expand Down Expand Up @@ -227,7 +225,7 @@ def get_reviewers() -> list:
reviewers = get_reviewers() or []
set_secret(_security_reviewers, reviewers)
print(
f"Successfully saved '{_security_reviewers}' as {reviewers}"
f"Successfully saved '{_security_reviewers}' as {reviewers} "
f"in your {secret_manager} secret manager."
)

Expand Down Expand Up @@ -319,10 +317,10 @@ def list_branches():
# Minimize FP
print("\n")
print("Few or no false positives are expected.")
print("\033[1mDo you want to prioritize avoiding false positives over maximum protection?\033[0m")
print("\033[1mDo you want to prioritize avoiding false-positives over maximum coverage?\033[0m")
print(
"Enter 'y' to run only 'ERROR' severity detections (less frequent) "
"and exclude 'WARNING' severity."
"Enter 'y' to run only 'ERROR' severity detectors (less frequent) "
"and exclude 'WARNING' severity detectors."
)
print(
"'ERROR' detections are typically related to dynamic execution, "
Expand Down
42 changes: 15 additions & 27 deletions setup/secret_managers/configure_cli.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import toml
import subprocess
import importlib.metadata
from getpass import getpass
Expand Down Expand Up @@ -114,32 +115,19 @@ def poetry_install_if_missing(package: str) -> None:


def add_to_toml(package: str) -> None:
with open("pyproject.toml", "r+") as toml_file:
lines = toml_file.readlines()
toml_file.seek(0)
package_name, package_version = package.split("==")
in_dependencies = False
added = False
new_lines = []

for line in lines:
if line.strip() == "[tool.poetry.dependencies]":
in_dependencies = True
new_lines.append(line)
elif in_dependencies and line.strip().startswith("["):
if not added:
new_lines.append(f'{package_name} = "{package_version}"\n')
added = True
new_lines.append(line)
in_dependencies = False
else:
new_lines.append(line)

if in_dependencies and not added:
new_lines.append(f'{package_name} = "{package_version}"\n')

toml_file.writelines(new_lines)
toml_file.truncate()
package_name, package_version = package.split("@")
addition = {package_name: package_version}

with open("pyproject.toml", "r") as toml_file:
data = toml.load(toml_file)

dependencies = data.get("tool", {}).get("poetry", {}).get("dependencies", {})
if package_name in dependencies:
return

dependencies.update(addition)
with open("pyproject.toml", "w") as toml_file:
toml.dump(data, toml_file)


# Install the secret manager's Python package, and add it to pyproject.toml
Expand Down Expand Up @@ -173,7 +161,7 @@ def choose_secrets_manager() -> str:
raise ValueError
except ValueError:
print("Invalid choice. Please run the script again and select a valid option.")
choose_secrets_manager()
return choose_secrets_manager()

sm_types = {
1: "vault",
Expand Down
2 changes: 2 additions & 0 deletions setup/setup_tool.py → setup/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ def main():
print("Make sure you setup TLS on another level (container, endpoint, gateway, etc.)")
rewrite_setting('APP_TLS', 'False')

print("\nSetup completed; the app is ready for use.")


if __name__ == "__main__":
main()
4 changes: 2 additions & 2 deletions src/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def get_app_root() -> str:


def rewrite_setting(setting_name: str, new_value: str) -> None:
file_path = f'{get_app_root()}/settings.py'
file_path = f'{get_app_root()}/src/settings.py'
if not os.path.exists(file_path):
raise FileNotFoundError(f"File not found: {file_path}")

Expand All @@ -54,6 +54,6 @@ def rewrite_setting(setting_name: str, new_value: str) -> None:


def write_setting(setting_name: str, new_value: str) -> None:
file_path = f'{get_app_root()}/settings.py'
file_path = f'{get_app_root()}/src/settings.py'
with open(file_path, 'a') as file:
file.write(f"{setting_name} = {new_value}\n")

0 comments on commit e08c772

Please sign in to comment.