From ca9af04ccd764a90c8fd449496c851725f759196 Mon Sep 17 00:00:00 2001 From: OM-JADHAV25 Date: Thu, 22 Jan 2026 16:14:55 +0530 Subject: [PATCH 1/9] Fix negative indexing --- backend/apps/github/api/internal/nodes/repository.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/apps/github/api/internal/nodes/repository.py b/backend/apps/github/api/internal/nodes/repository.py index 6a8c687d01..0f2cc5cbeb 100644 --- a/backend/apps/github/api/internal/nodes/repository.py +++ b/backend/apps/github/api/internal/nodes/repository.py @@ -17,6 +17,7 @@ RECENT_ISSUES_LIMIT = 5 RECENT_RELEASES_LIMIT = 5 +MAX_RECENT_MILESTONES_LIMIT = 100 @strawberry_django.type( @@ -69,6 +70,9 @@ def project( @strawberry_django.field(prefetch_related=["milestones"]) def recent_milestones(self, root: Repository, limit: int = 5) -> list[MilestoneNode]: """Resolve recent milestones.""" + if limit < 0: + return [] + limit = min(limit, MAX_RECENT_MILESTONES_LIMIT) return root.recent_milestones.order_by("-created_at")[:limit] @strawberry_django.field(prefetch_related=["releases"]) From 81c4484f64b5c629974f0a5ff136b1b1bc85fd7c Mon Sep 17 00:00:00 2001 From: OM-JADHAV25 Date: Thu, 22 Jan 2026 18:09:55 +0530 Subject: [PATCH 2/9] fix ReadTimeout crash --- backend/apps/owasp/api/internal/queries/project.py | 4 ++-- backend/apps/owasp/api/internal/queries/snapshot.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/apps/owasp/api/internal/queries/project.py b/backend/apps/owasp/api/internal/queries/project.py index af4db3c009..12285aeefd 100644 --- a/backend/apps/owasp/api/internal/queries/project.py +++ b/backend/apps/owasp/api/internal/queries/project.py @@ -8,10 +8,10 @@ from apps.owasp.api.internal.nodes.project import ProjectNode from apps.owasp.models.project import Project -MAX_RECENT_PROJECTS_LIMIT = 1000 +MAX_RECENT_PROJECTS_LIMIT = 100 MAX_SEARCH_QUERY_LENGTH = 100 MIN_SEARCH_QUERY_LENGTH = 3 -SEARCH_PROJECTS_LIMIT = 3 +SEARCH_PROJECTS_LIMIT = 100 @strawberry.type diff --git a/backend/apps/owasp/api/internal/queries/snapshot.py b/backend/apps/owasp/api/internal/queries/snapshot.py index d649acad34..51b06e2872 100644 --- a/backend/apps/owasp/api/internal/queries/snapshot.py +++ b/backend/apps/owasp/api/internal/queries/snapshot.py @@ -6,7 +6,7 @@ from apps.owasp.api.internal.nodes.snapshot import SnapshotNode from apps.owasp.models.snapshot import Snapshot -MAX_LIMIT = 100 +MAX_LIMIT = 10 @strawberry.type From bdf469379c0257aab5d95ccf3c190ff91b7d8e8e Mon Sep 17 00:00:00 2001 From: OM-JADHAV25 Date: Thu, 22 Jan 2026 19:13:53 +0530 Subject: [PATCH 3/9] Fix ReadTimeOut --- backend/apps/owasp/api/internal/queries/project.py | 8 +++++++- backend/apps/owasp/api/internal/queries/snapshot.py | 4 +++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/backend/apps/owasp/api/internal/queries/project.py b/backend/apps/owasp/api/internal/queries/project.py index 12285aeefd..14c947fd98 100644 --- a/backend/apps/owasp/api/internal/queries/project.py +++ b/backend/apps/owasp/api/internal/queries/project.py @@ -12,6 +12,7 @@ MAX_SEARCH_QUERY_LENGTH = 100 MIN_SEARCH_QUERY_LENGTH = 3 SEARCH_PROJECTS_LIMIT = 100 +MAX_PROJECT_KEY_LENGTH = 50 @strawberry.type @@ -29,8 +30,13 @@ def project(self, key: str) -> ProjectNode | None: ProjectNode | None: The project node if found, otherwise None. """ + normalized_key = key.strip() + + if not normalized_key or len(normalized_key) > MAX_PROJECT_KEY_LENGTH: + return None + try: - return Project.objects.get(key=f"www-project-{key}") + return Project.objects.get(key=f"www-project-{normalized_key}") except Project.DoesNotExist: return None diff --git a/backend/apps/owasp/api/internal/queries/snapshot.py b/backend/apps/owasp/api/internal/queries/snapshot.py index 51b06e2872..6d2353b471 100644 --- a/backend/apps/owasp/api/internal/queries/snapshot.py +++ b/backend/apps/owasp/api/internal/queries/snapshot.py @@ -30,7 +30,9 @@ def snapshots(self, limit: int = 12) -> list[SnapshotNode]: return ( Snapshot.objects.filter( status=Snapshot.Status.COMPLETED, - ).order_by( + ) + .only("id", "key", "title", "created_at") + .order_by( "-created_at", )[:limit] if (limit := min(limit, MAX_LIMIT)) > 0 From 460753813e279c5fd16fc6a3b8f7f655375a25a1 Mon Sep 17 00:00:00 2001 From: OM-JADHAV25 Date: Thu, 22 Jan 2026 20:32:25 +0530 Subject: [PATCH 4/9] Fix ReadTimeOut --- .../apps/owasp/api/internal/nodes/snapshot.py | 9 ++++-- .../owasp/api/internal/queries/project.py | 32 +++++++++++++------ .../owasp/api/internal/queries/snapshot.py | 13 ++++---- 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/backend/apps/owasp/api/internal/nodes/snapshot.py b/backend/apps/owasp/api/internal/nodes/snapshot.py index 4e45ab89eb..f5daac0b24 100644 --- a/backend/apps/owasp/api/internal/nodes/snapshot.py +++ b/backend/apps/owasp/api/internal/nodes/snapshot.py @@ -11,6 +11,9 @@ from apps.owasp.models.snapshot import Snapshot RECENT_ISSUES_LIMIT = 100 +RECENT_RELEASES_LIMIT = 50 +RECENT_USERS_LIMIT = 50 +RECENT_PROJECTS_LIMIT = 50 @strawberry_django.type( @@ -40,14 +43,14 @@ def new_issues(self, root: Snapshot) -> list[IssueNode]: @strawberry_django.field(prefetch_related=["new_projects"]) def new_projects(self, root: Snapshot) -> list[ProjectNode]: """Resolve new projects.""" - return root.new_projects.order_by("-created_at") + return root.new_projects.order_by("-created_at")[:RECENT_PROJECTS_LIMIT] @strawberry_django.field(prefetch_related=["new_releases"]) def new_releases(self, root: Snapshot) -> list[ReleaseNode]: """Resolve new releases.""" - return root.new_releases.order_by("-published_at") + return root.new_releases.order_by("-published_at")[:RECENT_RELEASES_LIMIT] @strawberry_django.field(prefetch_related=["new_users"]) def new_users(self, root: Snapshot) -> list[UserNode]: """Resolve new users.""" - return root.new_users.order_by("-created_at") + return root.new_users.order_by("-created_at")[:RECENT_USERS_LIMIT] diff --git a/backend/apps/owasp/api/internal/queries/project.py b/backend/apps/owasp/api/internal/queries/project.py index 14c947fd98..a745955640 100644 --- a/backend/apps/owasp/api/internal/queries/project.py +++ b/backend/apps/owasp/api/internal/queries/project.py @@ -36,7 +36,9 @@ def project(self, key: str) -> ProjectNode | None: return None try: - return Project.objects.get(key=f"www-project-{normalized_key}") + return Project.objects.only("id", "key", "name", "is_active", "created_at").get( + key=f"www-project-{normalized_key}" + ) except Project.DoesNotExist: return None @@ -51,10 +53,15 @@ def recent_projects(self, limit: int = 8) -> list[ProjectNode]: list[ProjectNode]: A list of recent active projects. """ - return ( - Project.objects.filter(is_active=True).order_by("-created_at")[:limit] - if (limit := min(limit, MAX_RECENT_PROJECTS_LIMIT)) > 0 - else [] + if limit <= 0: + return [] + + limit = min(limit, MAX_RECENT_PROJECTS_LIMIT) + + return list( + Project.objects.filter(is_active=True) + .only("id", "key", "name", "created_at", "is_active") + .order_by("-created_at")[:limit] ) @strawberry_django.field @@ -67,14 +74,21 @@ def search_projects(self, query: str) -> list[ProjectNode]: ): return [] - return Project.objects.filter( - is_active=True, - name__icontains=cleaned_query, - ).order_by("name")[:SEARCH_PROJECTS_LIMIT] + return list( + Project.objects.filter( + is_active=True, + name__icontains=cleaned_query, + ) + .only("id", "key", "name", "is_active") + .order_by("name")[:SEARCH_PROJECTS_LIMIT] + ) @strawberry_django.field def is_project_leader(self, info: strawberry.Info, login: str) -> bool: """Check if a GitHub login or name is listed as a project leader.""" + if not login or not login.strip(): + return False + try: github_user = GithubUser.objects.get(login=login) except GithubUser.DoesNotExist: diff --git a/backend/apps/owasp/api/internal/queries/snapshot.py b/backend/apps/owasp/api/internal/queries/snapshot.py index 6d2353b471..959fa89d8c 100644 --- a/backend/apps/owasp/api/internal/queries/snapshot.py +++ b/backend/apps/owasp/api/internal/queries/snapshot.py @@ -27,14 +27,15 @@ def snapshot(self, key: str) -> SnapshotNode | None: @strawberry_django.field def snapshots(self, limit: int = 12) -> list[SnapshotNode]: """Resolve snapshots.""" - return ( + if limit <= 0: + return [] + + limit = min(limit, MAX_LIMIT) + + return list( Snapshot.objects.filter( status=Snapshot.Status.COMPLETED, ) .only("id", "key", "title", "created_at") - .order_by( - "-created_at", - )[:limit] - if (limit := min(limit, MAX_LIMIT)) > 0 - else [] + .order_by("-created_at")[:limit] ) From fbe51b1c2788ff856f2903e0893761c17da1ef6a Mon Sep 17 00:00:00 2001 From: OM-JADHAV25 Date: Thu, 22 Jan 2026 21:19:04 +0530 Subject: [PATCH 5/9] Update for fuzz test fix --- .../apps/owasp/api/internal/nodes/snapshot.py | 8 ++++++-- .../owasp/api/internal/queries/project_test.py | 16 ++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/backend/apps/owasp/api/internal/nodes/snapshot.py b/backend/apps/owasp/api/internal/nodes/snapshot.py index f5daac0b24..8be560bbe6 100644 --- a/backend/apps/owasp/api/internal/nodes/snapshot.py +++ b/backend/apps/owasp/api/internal/nodes/snapshot.py @@ -14,6 +14,7 @@ RECENT_RELEASES_LIMIT = 50 RECENT_USERS_LIMIT = 50 RECENT_PROJECTS_LIMIT = 50 +RECENT_CHAPTERS_LIMIT = 50 @strawberry_django.type( @@ -28,8 +29,6 @@ class SnapshotNode(strawberry.relay.Node): """Snapshot node.""" - new_chapters: list[ChapterNode] = strawberry_django.field() - @strawberry_django.field def key(self, root: Snapshot) -> str: """Resolve key.""" @@ -54,3 +53,8 @@ def new_releases(self, root: Snapshot) -> list[ReleaseNode]: def new_users(self, root: Snapshot) -> list[UserNode]: """Resolve new users.""" return root.new_users.order_by("-created_at")[:RECENT_USERS_LIMIT] + + @strawberry_django.field(prefetch_related=["new_chapters"]) + def new_chapters(self, root: Snapshot) -> list[ChapterNode]: + """Resolve new chapters.""" + return root.new_chapters.order_by("-created_at")[:RECENT_CHAPTERS_LIMIT] diff --git a/backend/tests/apps/owasp/api/internal/queries/project_test.py b/backend/tests/apps/owasp/api/internal/queries/project_test.py index a5b05c6eb1..e7604c2c08 100644 --- a/backend/tests/apps/owasp/api/internal/queries/project_test.py +++ b/backend/tests/apps/owasp/api/internal/queries/project_test.py @@ -47,22 +47,26 @@ def mock_project(self): def test_resolve_project_existing(self, mock_project, mock_info): """Test resolving an existing project.""" - with patch("apps.owasp.models.project.Project.objects.get") as mock_get: - mock_get.return_value = mock_project + with patch("apps.owasp.models.project.Project.objects.get") as mock_only: + mock_qs = Mock() + mock_qs.get.return_value = mock_project + mock_only.return_value = mock_qs query = ProjectQuery() result = query.__class__.__dict__["project"](query, key="test-project") assert result == mock_project - mock_get.assert_called_once_with(key="www-project-test-project") + mock_qs.get.assert_called_once_with(key="www-project-test-project") def test_resolve_project_not_found(self, mock_info): """Test resolving a non-existent project.""" - with patch("apps.owasp.models.project.Project.objects.get") as mock_get: - mock_get.side_effect = Project.DoesNotExist + with patch("apps.owasp.models.project.Project.objects.get") as mock_only: + mock_qs = Mock() + mock_qs.get.side_effect = Project.DoesNotExist + mock_only.return_value = mock_qs query = ProjectQuery() result = query.__class__.__dict__["project"](query, key="non-existent") assert result is None - mock_get.assert_called_once_with(key="www-project-non-existent") + mock_qs.get.assert_called_once_with(key="www-project-non-existent") From a50724b114020d72c42b6d86f3b16002e2bc6f24 Mon Sep 17 00:00:00 2001 From: Om Jadhav Date: Thu, 22 Jan 2026 21:37:01 +0530 Subject: [PATCH 6/9] Fix test to use Project.objects.only instead of get --- backend/tests/apps/owasp/api/internal/queries/project_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/tests/apps/owasp/api/internal/queries/project_test.py b/backend/tests/apps/owasp/api/internal/queries/project_test.py index e7604c2c08..460e6a7350 100644 --- a/backend/tests/apps/owasp/api/internal/queries/project_test.py +++ b/backend/tests/apps/owasp/api/internal/queries/project_test.py @@ -47,7 +47,7 @@ def mock_project(self): def test_resolve_project_existing(self, mock_project, mock_info): """Test resolving an existing project.""" - with patch("apps.owasp.models.project.Project.objects.get") as mock_only: + with patch("apps.owasp.models.project.Project.objects.only") as mock_only: mock_qs = Mock() mock_qs.get.return_value = mock_project mock_only.return_value = mock_qs @@ -60,7 +60,7 @@ def test_resolve_project_existing(self, mock_project, mock_info): def test_resolve_project_not_found(self, mock_info): """Test resolving a non-existent project.""" - with patch("apps.owasp.models.project.Project.objects.get") as mock_only: + with patch("apps.owasp.models.project.Project.objects.only") as mock_only: mock_qs = Mock() mock_qs.get.side_effect = Project.DoesNotExist mock_only.return_value = mock_qs From 082afa99529165a8e01621f1a0f0fb3a66286458 Mon Sep 17 00:00:00 2001 From: Om Jadhav Date: Thu, 22 Jan 2026 22:11:36 +0530 Subject: [PATCH 7/9] Change MAX_LIMIT from 1000 to 100 --- backend/apps/owasp/api/internal/nodes/project.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/apps/owasp/api/internal/nodes/project.py b/backend/apps/owasp/api/internal/nodes/project.py index f5da84653f..922c577f19 100644 --- a/backend/apps/owasp/api/internal/nodes/project.py +++ b/backend/apps/owasp/api/internal/nodes/project.py @@ -20,7 +20,7 @@ RECENT_RELEASES_LIMIT = 5 RECENT_PULL_REQUESTS_LIMIT = 5 -MAX_LIMIT = 1000 +MAX_LIMIT = 100 @strawberry_django.type( @@ -54,7 +54,7 @@ def health_metrics_list( ) -> list[ProjectHealthMetricsNode]: """Resolve project health metrics.""" return ( - root.health_metrics.order_by("nest_created_at")[:limit] + root.health_metrics.order_by("-nest_created_at")[:limit] if (limit := min(limit, MAX_LIMIT)) > 0 else [] ) From 25fb6a206e9da8d25570cebc78f27e2f54fedcb3 Mon Sep 17 00:00:00 2001 From: Om Jadhav Date: Thu, 22 Jan 2026 22:35:05 +0530 Subject: [PATCH 8/9] Adjust limit handling in snapshot query --- backend/apps/owasp/api/internal/queries/snapshot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/apps/owasp/api/internal/queries/snapshot.py b/backend/apps/owasp/api/internal/queries/snapshot.py index 959fa89d8c..5bc8fd0943 100644 --- a/backend/apps/owasp/api/internal/queries/snapshot.py +++ b/backend/apps/owasp/api/internal/queries/snapshot.py @@ -30,7 +30,7 @@ def snapshots(self, limit: int = 12) -> list[SnapshotNode]: if limit <= 0: return [] - limit = min(limit, MAX_LIMIT) + limit = min(max(limit, 1), MAX_LIMIT) return list( Snapshot.objects.filter( From ff7b94497b2cd068fa8a95d789b27c174d45aab4 Mon Sep 17 00:00:00 2001 From: Om Jadhav Date: Thu, 22 Jan 2026 22:36:25 +0530 Subject: [PATCH 9/9] Adjust limit handling in project query function --- backend/apps/owasp/api/internal/queries/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/apps/owasp/api/internal/queries/project.py b/backend/apps/owasp/api/internal/queries/project.py index a745955640..667124cf5b 100644 --- a/backend/apps/owasp/api/internal/queries/project.py +++ b/backend/apps/owasp/api/internal/queries/project.py @@ -56,7 +56,7 @@ def recent_projects(self, limit: int = 8) -> list[ProjectNode]: if limit <= 0: return [] - limit = min(limit, MAX_RECENT_PROJECTS_LIMIT) + limit = min(max(limit, 1), MAX_RECENT_PROJECTS_LIMIT) return list( Project.objects.filter(is_active=True)