Skip to content

Commit c6be2b0

Browse files
authored
Merge branch 'main' into feat/gsoc-page
2 parents 93ce158 + f3a86cd commit c6be2b0

29 files changed

+2506
-1186
lines changed

.github/copilot-instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ OWASP BLT is a Django-based web application for bug bounty management and securi
2323

2424
### Required Before Each Commit
2525

26-
- **ALWAYS** run `pre-commit run --all-files` before committing any changes
26+
- **ALWAYS** run `pre-commit run --all-files` before committing any changes - don't run this when iterating locally, only before committing
2727
- This will run automatic formatters and linters including:
2828
- Black (code formatting)
2929
- isort (import sorting)

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ repos:
6060
entry: bash -c 'if [ -z "$GITHUB_ACTIONS" ]; then poetry run python manage.py test --failfast; fi'
6161
language: system
6262
types: [python]
63+
stages: [pre-push]
6364
pass_filenames: false
6465
always_run: true
6566
verbose: true

blt/middleware/throttling.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ def __init__(self, get_response):
2828
logger.info("ThrottlingMiddleware initialized with limits: %s", self.THROTTLE_LIMITS)
2929

3030
def __call__(self, request):
31+
# Bypass all throttling without logging when DEBUG is True
32+
if settings.DEBUG:
33+
return self.get_response(request)
34+
3135
ip = self.get_client_ip(request)
3236
method = request.method
3337
path = request.path

blt/settings.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,11 @@
264264
EMAIL_PORT = 1025
265265

266266
# Set the custom email backend that sends Slack notifications
267-
EMAIL_BACKEND = "blt.mail.SlackNotificationEmailBackend"
267+
# Use console backend in debug mode to print emails to terminal
268+
if DEBUG:
269+
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
270+
else:
271+
EMAIL_BACKEND = "blt.mail.SlackNotificationEmailBackend"
268272

269273
REPORT_EMAIL = os.environ.get("REPORT_EMAIL", "blank")
270274
REPORT_EMAIL_PASSWORD = os.environ.get("REPORT_PASSWORD", "blank")

blt/urls.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@
233233
Listbounties,
234234
OngoingHunts,
235235
OrganizationDetailView,
236+
OrganizationListModeView,
236237
OrganizationListView,
237238
OrganizationSettings,
238239
PreviousHunts,
@@ -269,6 +270,7 @@
269270
organization_dashboard_hunt_detail,
270271
organization_dashboard_hunt_edit,
271272
organization_hunt_results,
273+
refresh_organization_repos_api,
272274
room_messages_api,
273275
send_message_api,
274276
sizzle,
@@ -1021,6 +1023,7 @@
10211023
path("sponsor/", sponsor_view, name="sponsor"),
10221024
path("donate/", donate_view, name="donate"),
10231025
path("organizations/", OrganizationListView.as_view(), name="organizations"),
1026+
path("organizations/list/", OrganizationListModeView.as_view(), name="organizations_list_mode"),
10241027
path("map/", MapView.as_view(), name="map"),
10251028
path("domains/", DomainListView.as_view(), name="domains"),
10261029
path("trademarks/", trademark_search, name="trademark_search"),
@@ -1165,6 +1168,9 @@
11651168
path("add_repo", add_repo, name="add_repo"),
11661169
path("organization/<slug:slug>/", OrganizationDetailView.as_view(), name="organization_detail"),
11671170
path("organization/<slug:slug>/update-repos/", update_organization_repos, name="update_organization_repos"),
1171+
path(
1172+
"api/organization/<int:org_id>/refresh/", refresh_organization_repos_api, name="refresh_organization_repos_api"
1173+
),
11681174
# GitHub Issues
11691175
path("github-issues/<int:pk>/", GitHubIssueDetailView.as_view(), name="github_issue_detail"),
11701176
path("github-issues/", GitHubIssuesView.as_view(), name="github_issues"),

poetry.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

project_channels.csv

Lines changed: 0 additions & 114 deletions
This file was deleted.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ drf-yasg = "^1.21.10"
6262
slack-bolt = "^1.23.0"
6363
slack-sdk = "^3.35.0"
6464
tld = "0.13"
65-
openai = "^2.9.0"
65+
openai = "^2.11.0"
6666
async-timeout = "^4.0.3"
6767
python-dateutil = "^2.9.0.post0"
6868
pyzipper = "^0.3.6"

run.sh

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,24 @@ if [ ! -f ./ssl/cert.pem ] || [ ! -f ./ssl/key.pem ]; then
1313
echo "Self-signed certificates generated successfully."
1414
fi
1515

16-
# Run the application with SSL
17-
uvicorn blt.asgi:application --host 0.0.0.0 --port 8443 --ssl-keyfile ./ssl/key.pem --ssl-certfile ./ssl/cert.pem --log-level debug --reload --reload-include *.html
16+
# Run migrations
17+
echo "Checking and applying migrations..."
18+
poetry run python manage.py migrate
19+
20+
# Open browser after a short delay (in background)
21+
(
22+
sleep 3
23+
URL="https://localhost:8443"
24+
if command -v xdg-open >/dev/null 2>&1; then
25+
xdg-open "$URL"
26+
elif command -v open >/dev/null 2>&1; then
27+
open "$URL"
28+
elif command -v start >/dev/null 2>&1; then
29+
start "$URL"
30+
else
31+
echo "Please open $URL in your browser."
32+
fi
33+
) &
34+
35+
# Run the application with SSL in poetry shell
36+
poetry run uvicorn blt.asgi:application --host 0.0.0.0 --port 8443 --ssl-keyfile ./ssl/key.pem --ssl-certfile ./ssl/cert.pem --log-level debug --reload --reload-include *.html
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
from urllib.parse import urlparse
2+
3+
from website.management.base import LoggedBaseCommand
4+
from website.models import Organization
5+
6+
7+
class Command(LoggedBaseCommand):
8+
help = "Populate github_org field from source_code field for organizations"
9+
10+
def add_arguments(self, parser):
11+
parser.add_argument(
12+
"--dry-run",
13+
action="store_true",
14+
help="Show what would be updated without making changes",
15+
)
16+
parser.add_argument(
17+
"--overwrite",
18+
action="store_true",
19+
help="Overwrite existing github_org values",
20+
)
21+
22+
def handle(self, *args, **options):
23+
dry_run = options["dry_run"]
24+
overwrite = options["overwrite"]
25+
26+
self.stdout.write(self.style.SUCCESS("Starting GitHub organization population..."))
27+
28+
# Get organizations with source_code but no github_org (or all if overwrite)
29+
if overwrite:
30+
orgs = Organization.objects.filter(source_code__isnull=False).exclude(source_code="")
31+
self.stdout.write(f"Processing {orgs.count()} organizations (overwrite mode)...")
32+
else:
33+
orgs = Organization.objects.filter(source_code__isnull=False, github_org__isnull=True).exclude(
34+
source_code=""
35+
) | Organization.objects.filter(source_code__isnull=False, github_org="").exclude(source_code="")
36+
self.stdout.write(f"Processing {orgs.count()} organizations without github_org...")
37+
38+
updated_count = 0
39+
skipped_count = 0
40+
error_count = 0
41+
42+
for org in orgs:
43+
try:
44+
github_org = self.extract_github_org(org.source_code)
45+
46+
if github_org:
47+
if dry_run:
48+
self.stdout.write(
49+
f"[DRY RUN] Would set '{org.name}' github_org to: {github_org} (from {org.source_code})"
50+
)
51+
updated_count += 1
52+
else:
53+
org.github_org = github_org
54+
org.save(update_fields=["github_org"])
55+
self.stdout.write(
56+
self.style.SUCCESS(
57+
f"✓ Updated '{org.name}' github_org to: {github_org} (from {org.source_code})"
58+
)
59+
)
60+
updated_count += 1
61+
else:
62+
self.stdout.write(
63+
self.style.WARNING(f"⚠ Could not extract GitHub org from '{org.source_code}' for '{org.name}'")
64+
)
65+
skipped_count += 1
66+
67+
except Exception as e:
68+
self.stdout.write(self.style.ERROR(f"✗ Error processing '{org.name}': {str(e)}"))
69+
error_count += 1
70+
71+
# Summary
72+
self.stdout.write("\n" + "=" * 60)
73+
self.stdout.write(self.style.SUCCESS("Summary:"))
74+
if dry_run:
75+
self.stdout.write(f" Organizations that would be updated: {updated_count}")
76+
else:
77+
self.stdout.write(f" Organizations updated: {updated_count}")
78+
self.stdout.write(f" Organizations skipped: {skipped_count}")
79+
self.stdout.write(f" Errors: {error_count}")
80+
self.stdout.write("=" * 60)
81+
82+
if dry_run:
83+
self.stdout.write(self.style.WARNING("\nThis was a dry run. No changes were made."))
84+
self.stdout.write("Run without --dry-run to apply changes.")
85+
86+
def extract_github_org(self, url):
87+
"""
88+
Extract GitHub organization name from various GitHub URL formats.
89+
90+
Supports:
91+
- https://github.com/org
92+
- https://github.com/org/
93+
- https://github.com/org/repo
94+
- https://github.com/org/repo/...
95+
- http://github.com/org
96+
- www.github.com/org
97+
- github.com/org
98+
"""
99+
if not url:
100+
return None
101+
102+
try:
103+
# Handle URLs without scheme
104+
if not url.startswith(("http://", "https://")):
105+
url = "https://" + url
106+
107+
parsed = urlparse(url)
108+
109+
# Check if it's a GitHub URL
110+
if "github.com" not in parsed.netloc:
111+
return None
112+
113+
# Extract path parts
114+
path_parts = [p for p in parsed.path.split("/") if p]
115+
116+
# GitHub org is the first part of the path
117+
if path_parts:
118+
return path_parts[0]
119+
120+
return None
121+
122+
except Exception:
123+
return None

0 commit comments

Comments
 (0)