Skip to content

Fix negative indexing in recentProjects and recentMilestones#3478

Merged
arkid15r merged 12 commits intoOWASP:mainfrom
anurag2787:fix/negative-indexing
Feb 3, 2026
Merged

Fix negative indexing in recentProjects and recentMilestones#3478
arkid15r merged 12 commits intoOWASP:mainfrom
anurag2787:fix/negative-indexing

Conversation

@anurag2787
Copy link
Contributor

@anurag2787 anurag2787 commented Jan 22, 2026

Proposed change

This PR fixes an issue where passing a negative value to the limit parameter could crash the recentProjects -> repositories -> recentMilestones GraphQL query.

The affected resolvers now check and sanitize the limit value before using it to slice Django querysets. If the value is negative or invalid, an empty list is returned instead of causing an error.

This ensures the API no longer crashes when fuzzing or unexpected inputs are used.

Resolves #3414

image

Checklist

  • Required: I followed the contributing workflow
  • Required: I verified that my code works as intended and resolves the issue as described
  • Required: I ran make check-test locally: all warnings addressed, tests passed
  • I used AI for code, documentation, tests, or communication related to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 22, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Introduced standardized limit validation across API queries and nodes to gracefully handle invalid or out-of-range limit parameters
    • API endpoints now return empty results when limit validation fails instead of potentially causing errors
    • Limit parameters are consistently clamped to a maximum threshold across all affected queries
  • Tests

    • Added comprehensive test coverage for limit validation functionality, including edge cases and invalid input handling

Walkthrough

This PR introduces a centralized normalize_limit utility function to validate and clamp GraphQL query limit parameters across the backend. The function handles type coercion, rejects non-positive values, and caps limits at a maximum threshold, addressing negative indexing errors by returning empty lists for invalid inputs.

Changes

Cohort / File(s) Summary
Utility Function & Tests
backend/apps/common/utils.py, backend/tests/apps/common/utils_test.py
Added normalize_limit(limit: int, max_limit: int = 1000) -> int | None function that coerces limit to int, validates it's positive, and returns clamped value or None. Includes comprehensive test coverage for valid limits, invalid types, and edge cases.
GitHub API Queries
backend/apps/github/api/internal/queries/{issue, pull_request, release, repository, repository_contributor}.py
Applied normalize_limit validation at query entry points; returns empty list if normalization yields None, otherwise uses normalized_limit for queryset slicing.
GitHub API Nodes
backend/apps/github/api/internal/nodes/repository.py
Added MAX_LIMIT constant and normalize_limit validation in recent_milestones; returns empty list for invalid limits or uses validated_limit for ordering and slicing.
OWASP API Queries
backend/apps/owasp/api/internal/queries/{board_of_directors, chapter, event, member_snapshot, post, snapshot}.py
Applied normalize_limit validation pattern across resolvers; returns empty list if normalization fails, otherwise queries with normalized_limit-bounded results.
OWASP API Queries & Nodes
backend/apps/owasp/api/internal/queries/project.py, backend/apps/owasp/api/internal/nodes/project.py
Introduced MAX_LIMIT constant and normalize_limit validation in both recent_projects and related node methods (health_metrics_list, recent_milestones).
Mentorship API
backend/apps/mentorship/api/internal/{queries/mentorship, queries/program, nodes/module}.py
Added MAX_LIMIT and normalize_limit validation across mentorship-related queries and nodes; ensures limits are validated before use in slicing or pagination calculations.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • arkid15r
  • kasya
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: fixing negative indexing issues in recentProjects and recentMilestones resolvers by sanitizing limit parameters.
Description check ✅ Passed The description provides relevant context about the issue, the fix implemented, and relates directly to the changeset of sanitizing limit values across multiple resolvers.
Linked Issues check ✅ Passed The PR successfully addresses issue #3414 by implementing normalize_limit() function and applying it across all affected resolvers (recentProjects, recentMilestones, and related queries) to prevent negative indexing crashes.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing the normalize_limit utility and applying it consistently across affected resolvers. No unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 22, 2026
@anurag2787 anurag2787 marked this pull request as ready for review January 22, 2026 20:16
Copy link
Collaborator

@arkid15r arkid15r left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we extract the repetitive code into something reusable?

coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 28, 2026
@anurag2787 anurag2787 requested a review from arkid15r January 28, 2026 04:38
coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 29, 2026
coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 29, 2026
@arkid15r
Copy link
Collaborator

@ahmedxgouda could you verify the fix?

@arkid15r arkid15r enabled auto-merge January 30, 2026 00:09
coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 30, 2026
Copy link
Collaborator

@ahmedxgouda ahmedxgouda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work. All things work. Just asking to use the new function in all queries and nodes' fields that use limit (e.g. recentIssues).

auto-merge was automatically disabled February 2, 2026 17:14

Head branch was pushed to by a user without write access

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/apps/mentorship/api/internal/queries/mentorship.py (1)

102-130: ⚠️ Potential issue | 🟠 Major

Guard negative/invalid offset to fully prevent negative slicing.

offset is user-controlled; a negative value will raise Django's ValueError: Negative indexing is not supported during slicing, which Django explicitly disallows because QuerySets translate to SQL LIMIT/OFFSET and cannot support negative indices. Validate/clamp offset the same way as limit to handle invalid input gracefully instead of crashing.

✅ Suggested fix
         if (normalized_limit := normalize_limit(limit, MAX_LIMIT)) is None:
             return []
 
+        try:
+            offset = int(offset)
+        except (TypeError, ValueError):
+            return []
+        if offset < 0:
+            return []
+
         try:
             module = Module.objects.only("id").get(key=module_key, program__key=program_key)
@@
-            issues = issues_qs[offset : offset + normalized_limit]
+            issues = issues_qs[offset : offset + normalized_limit]
🧹 Nitpick comments (1)
backend/apps/mentorship/api/internal/nodes/module.py (1)

85-95: The offset parameter is not validated.

While the limit is correctly normalized, a negative offset value could still cause issues with the slice [offset : offset + normalized_limit]. Consider validating offset similarly (e.g., max(0, offset)).

♻️ Suggested fix
     def issues(
         self, limit: int = 20, offset: int = 0, label: str | None = None
     ) -> list[IssueNode]:
         """Return paginated issues linked to this module, optionally filtered by label."""
         if (normalized_limit := normalize_limit(limit, MAX_LIMIT)) is None:
             return []
 
+        offset = max(0, offset)
+
         queryset = self.issues.select_related("repository", "author").prefetch_related(
             "assignees", "labels"
         )

@arkid15r
Copy link
Collaborator

arkid15r commented Feb 3, 2026

@cubic-dev-ai review this pr

@cubic-dev-ai
Copy link
Contributor

cubic-dev-ai bot commented Feb 3, 2026

@cubic-dev-ai review this pr

@arkid15r I have started the AI code review. It will take a few minutes to complete.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 20 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Feb 3, 2026

@codecov
Copy link

codecov bot commented Feb 3, 2026

Codecov Report

❌ Patch coverage is 46.37681% with 37 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.50%. Comparing base (3be7ae6) to head (3fc8826).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
backend/apps/owasp/api/internal/nodes/project.py 16.66% 5 Missing ⚠️
backend/apps/owasp/api/internal/queries/chapter.py 25.00% 3 Missing ⚠️
backend/apps/owasp/api/internal/queries/event.py 25.00% 3 Missing ⚠️
backend/apps/owasp/api/internal/queries/post.py 25.00% 3 Missing ⚠️
backend/apps/owasp/api/internal/queries/project.py 25.00% 3 Missing ⚠️
...ackend/apps/owasp/api/internal/queries/snapshot.py 25.00% 3 Missing ⚠️
...ckend/apps/github/api/internal/nodes/repository.py 60.00% 1 Missing and 1 partial ⚠️
backend/apps/github/api/internal/queries/issue.py 50.00% 1 Missing and 1 partial ⚠️
...kend/apps/github/api/internal/queries/milestone.py 50.00% 1 Missing and 1 partial ⚠️
...d/apps/github/api/internal/queries/pull_request.py 50.00% 1 Missing and 1 partial ⚠️
... and 5 more
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #3478      +/-   ##
==========================================
- Coverage   87.67%   87.50%   -0.17%     
==========================================
  Files         462      462              
  Lines       14309    14363      +54     
  Branches     1910     1926      +16     
==========================================
+ Hits        12545    12569      +24     
- Misses       1346     1368      +22     
- Partials      418      426       +8     
Flag Coverage Δ
backend 87.23% <46.37%> (-0.22%) ⬇️
frontend 88.27% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
backend/apps/common/utils.py 97.26% <100.00%> (+0.33%) ⬆️
...hub/api/internal/queries/repository_contributor.py 85.71% <50.00%> (+1.09%) ⬆️
...ckend/apps/github/api/internal/nodes/repository.py 95.55% <60.00%> (-4.45%) ⬇️
backend/apps/github/api/internal/queries/issue.py 91.66% <50.00%> (-8.34%) ⬇️
...kend/apps/github/api/internal/queries/milestone.py 94.11% <50.00%> (-5.89%) ⬇️
...d/apps/github/api/internal/queries/pull_request.py 87.50% <50.00%> (-5.61%) ⬇️
...ackend/apps/github/api/internal/queries/release.py 90.90% <50.00%> (-9.10%) ⬇️
...end/apps/github/api/internal/queries/repository.py 89.47% <50.00%> (-10.53%) ⬇️
...s/owasp/api/internal/queries/board_of_directors.py 89.47% <50.00%> (-10.53%) ⬇️
...apps/owasp/api/internal/queries/member_snapshot.py 90.00% <50.00%> (-6.30%) ⬇️
... and 6 more

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 3be7ae6...3fc8826. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@arkid15r arkid15r enabled auto-merge February 3, 2026 19:22
@arkid15r arkid15r added this pull request to the merge queue Feb 3, 2026
Merged via the queue into OWASP:main with commit eb3cbb0 Feb 3, 2026
33 of 35 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Feb 12, 2026
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Solve recentProjects query negative indexing

3 participants

Comments