Skip to content
Open
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
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ CVE_USERNAME="user@example.org"
CVE_API_KEY="123456"
CVE_ENV="testproddev"
CVE_ENABLED_REPOS="python/cpython"
REQUIRED_ORG="python"
SENTRY_DSN="{PROTOCOL}://{PUBLIC_KEY}:{SECRET_KEY}@{HOST}{PATH}/{PROJECT_ID}"
1 change: 1 addition & 0 deletions .github/workflows/cron.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,5 @@ jobs:
CVE_API_KEY: ${{ secrets.CVE_API_KEY }}
CVE_ENV: ${{ vars.CVE_ENV }}
CVE_ENABLED_REPOS: ${{ vars.CVE_ENABLED_REPOS }}
REQUIRED_ORG: ${{ vars.REQUIRED_ORG }}
SENTRY_DSN: ${{ github.event_name == 'schedule' && secrets.SENTRY_DSN || '' }}
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ and, for every advisory it closes ones marked as completed, promotes accepted on
from triage to draft, reserves CVE IDs, creates private forks, and adds the
PSRT team as collaborators.

It processes every repository the GitHub App is installed on.
It only processes installations owned by the account named in the `REQUIRED_ORG`
environment variable; installations under any other user are skipped.
For that account it processes every repository the GitHub App is installed on.
The process is identical across repositories except that CVE IDs are only
reserved for repositories listed in the `CVE_ENABLED_REPOS` environment variable.

Expand Down
9 changes: 8 additions & 1 deletion src/psrt_ghsa_bot/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,15 +223,22 @@ def run() -> None:
name.strip() for name in (os.environ.get("CVE_ENABLED_REPOS") or "python/cpython").split(",") if name.strip()
)

required_org = os.environ["REQUIRED_ORG"]

print("Fetching installations...")
# Apply to all repositories for each installation.
installations = github.rest.paginate(
github.rest.apps.list_installations,
)
installation_count = 0
for installation_data in installations:
account_login = installation_data.account.login
if account_login.lower() != required_org.lower():
print(f"\n⏭️ Skipping installation for {account_login!r} (not {required_org!r})")
continue

installation_count += 1
print(f"\nProcessing installation {installation_count}: {installation_data.account.login}")
print(f"\nProcessing installation {installation_count}: {account_login}")

installation_github = github.with_auth(
github.auth.as_installation(installation_data.id),
Expand Down
24 changes: 24 additions & 0 deletions tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,27 @@ def test_reserve_one_cve_id(cve_reserve_response, cve_id, year) -> None:
assert app.reserve_one_cve(cve_api) == cve_id

cve_api.reserve.assert_called_with(count=1, year=year, random=True)


def test_only_processes_installations_for_required_org(monkeypatch) -> None:
monkeypatch.setenv("GH_CLIENT_ID", "123")
monkeypatch.setenv("GH_CLIENT_PRIVATE_KEY", "a2V5")
monkeypatch.setenv("CVE_USERNAME", "stan@python.org")
monkeypatch.setenv("CVE_API_KEY", "key")
monkeypatch.setenv("REQUIRED_ORG", "python")

github = mock.Mock()
github.rest.paginate.return_value = [
mock.Mock(id=1, account=mock.Mock(login="evil-org")),
mock.Mock(id=2, account=mock.Mock(login="python")),
]
github.with_auth.return_value.rest.paginate.return_value = []

with (
mock.patch("psrt_ghsa_bot.app.GitHub", return_value=github),
mock.patch("psrt_ghsa_bot.app.CveApi"),
mock.patch("psrt_ghsa_bot.app.apply_to_repo"),
):
app.run()

github.auth.as_installation.assert_called_once_with(2)