Skip to content

Conversation

@ahmedxgouda
Copy link
Collaborator

Proposed change

Resolves #(put the issue number here)

Add the PR description here.

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

ahmedxgouda and others added 8 commits January 16, 2026 11:33
* Add dockerfile and compose and update makefile

* Dump db-data

* Reorder makefile

* Dump db-data

* Remove e2e dockerfile

* Add CI/CD

* Apply rabbit's suggestions

* Add postgres instead of pgvector

* Remove needs

* Update envs

* Fix migrations step

* Add envs to docker

* Remove migrations step

* Remove --without test from dockerfile

* Copy tests in dockerfile and add needs to gh workflow

* Update dockerfile

* Apply suggestion

* Use the e2e instance in the frontend e2e tests

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD and dockerfile

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Restore needs for the job

* Update Makefiles

* Update docs

* Apply suggestions

* Update load-data

* Skip sonar error

* update port numbers

* Update docs

* Update code

* Add required shell property back

* Bump config.webServer timeout

* Update code

---------

Co-authored-by: Arkadii Yakovets <[email protected]>
* Add DB environment variables to e2e.yaml and add csrf_decorate function

* Skip sonar suggestion

* Fix rest api internal error

* Add timeout

* Update docs

* Update code

* Revert csrf update

* Add command to dump local data

* Update dump and load data

* Update rest api config and docs

* Apply check-spelling

* Use .env.e2e.example for frontend e2e tests in gh actions

* Apply rabbit's suggestions

* Migrate dump_data to django command and dump owasp, github, and slack tables only

* Apply rabbit's suggestions

* Update code

* Refactor dump_data

* Use local cache for e2e

* Remove old load_data command

* Add tests

* Skip sonar

* Apply rabbit suggestions

* Update code

---------

Co-authored-by: Arkadii Yakovets <[email protected]>

Add backend/data/nest.sql.gz
* Add hypothesis to poetry and fuzz test the index endpoint.

* Refactor import statements in algolia_test.py for consistency

* Add the client ip address to the request META.

* Add fuzz testing for Slack event handlers and refactor algolia fuzz testing.

* Refactor fuzz tests for Slack event handlers to improve readability and structure

* Fix the poetry lock file.

* Remove fuzz testing from algolia_search unit tests

* Create a docker file for fuzz-testing, add the run commands to the MakeFile and install model_bakery for creating randomized data for all models.

* Refactor to improve quality

* Update fuzz testing setup: modify Makefile and Dockerfile, add entrypoint script for environment configuration

* Update poetry.lock to reflect dependency changes and version updates

* Create a fuzz configuration, update docker file, makefile, and the tests.

* Refactor fuzz configuration by reorganizing imports and cleaning up whitespace

* Update Dockerfile and entrypoint script to use Alpine base image and shell

* Run the server on port 8000 after the tests.

* Create a docker compose file for fuzz testing.

* Add 'graphqler' to custom dictionary

* Load data from nest.json and add graphqler to cspell dict.

* Remove model-bakery dependency from pyproject.toml and update poetry.lock

* Update graphqler command in docker compose and the healthcheck

* Update graphql command to use backend service URL in docker-compose

* Refactor docker-compose to build graphqler service from Dockerfile and add entrypoint script for fuzzing tests

* Enhance fuzz testing setup: update Dockerfile and entrypoint scripts, improve CSRF handling, and refine healthcheck command in docker-compose

* Update fuzz-test-backend command to abort on container exit

* Add fuzz testing workflow and update image build steps

* Add .env file creation step for fuzz tests in CI/CD workflow

* Add Docker Hub login step for fuzz tests in CI/CD workflow

* Refactor for the checks

* Refactor fuzz testing workflow: replace Docker Hub login with buildx setup and update docker-compose handling

* Fix fuzz tests workflow: rename docker-compose file

* Refactor fuzz-tests job.

* Add environment variables for fuzz tests configuration

* Update fuzz tests environment variables

* Fix poetry lock file

* Sort the custom-dict.

* Update content hash in poetry.lock

* Add docker cache mounts to the backend image

* Add Redis configuration

* refactor yaml

* Add docker cache mounts to graphql file

* Remove unnecessary chmod command for cache directories in Dockerfile

* Fix poetry lock file

* Add cache mounts to backend tests

* Update cache mounts in graphql image

* Update mkdir in graphql image

* Remove duplicates

* Update tests

* Rename docker compose

* Update poetry lock

* Apply sonar

* Migrate to OWASP repo

* Update docker

* Use graphqler maintainer docker image

* Add disable permissions, update docker compose, and update entrypoint

* Establish an e2e backend instance locally and in CI/CD (#2429)

* Add dockerfile and compose and update makefile

* Dump db-data

* Reorder makefile

* Dump db-data

* Remove e2e dockerfile

* Add CI/CD

* Apply rabbit's suggestions

* Add postgres instead of pgvector

* Remove needs

* Update envs

* Fix migrations step

* Add envs to docker

* Remove migrations step

* Remove --without test from dockerfile

* Copy tests in dockerfile and add needs to gh workflow

* Update dockerfile

* Apply suggestion

* Use the e2e instance in the frontend e2e tests

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD and dockerfile

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Restore needs for the job

* Update Makefiles

* Update docs

* Apply suggestions

* Update load-data

* Skip sonar error

* update port numbers

* Update docs

* Update code

* Add required shell property back

* Bump config.webServer timeout

* Update code

---------

Co-authored-by: Arkadii Yakovets <[email protected]>

* Fix running e2e backend (#2710)

* Add DB environment variables to e2e.yaml and add csrf_decorate function

* Skip sonar suggestion

* Fix rest api internal error

* Add timeout

* Update docs

* Update code

* Revert csrf update

* Add command to dump local data

* Update dump and load data

* Update rest api config and docs

* Apply check-spelling

* Use .env.e2e.example for frontend e2e tests in gh actions

* Apply rabbit's suggestions

* Migrate dump_data to django command and dump owasp, github, and slack tables only

* Apply rabbit's suggestions

* Update code

* Refactor dump_data

* Use local cache for e2e

* Remove old load_data command

* Add tests

* Skip sonar

* Apply rabbit suggestions

* Update code

---------

Co-authored-by: Arkadii Yakovets <[email protected]>

* Update code for e2e

* Add runs-on

* Skip sonar and fix ci/cd

* Apply rabbit suggestion and override entrypoint in ci/cd

* Use env with csrf

* Add timeout

* Remove hypothesis and old test files

* Apply rabbit's suggestions

* Update ci/cd and makefile

* Use digest pinning with graphqler image

* Update dockerfile and fix the typeerror issue

* Apply sonar suggestion

* Apply sonar and rabbit suggestions

* Remove cache from ci/cd

* Use curl instead of wget

* Separate e2e from fuzz

* Update fuzz ci/cd

* Update CI/CD

* Run precommit

* Update code

* Update code

* Update docs, ci/cd, and apply suggestions

* Use digest pinning and parameters in workflow

* Apply sonar suggestions

* Apply rabbit suggestions

* Run migrations for fuzz testing in ci/cd

* Apply rabbit suggestions

* Fix exceptions

* Establish an e2e backend instance locally and in CI/CD (#2429)

* Add dockerfile and compose and update makefile

* Dump db-data

* Reorder makefile

* Dump db-data

* Remove e2e dockerfile

* Add CI/CD

* Apply rabbit's suggestions

* Add postgres instead of pgvector

* Remove needs

* Update envs

* Fix migrations step

* Add envs to docker

* Remove migrations step

* Remove --without test from dockerfile

* Copy tests in dockerfile and add needs to gh workflow

* Update dockerfile

* Apply suggestion

* Use the e2e instance in the frontend e2e tests

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD and dockerfile

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Restore needs for the job

* Update Makefiles

* Update docs

* Apply suggestions

* Update load-data

* Skip sonar error

* update port numbers

* Update docs

* Update code

* Add required shell property back

* Bump config.webServer timeout

* Update code

---------

Co-authored-by: Arkadii Yakovets <[email protected]>

* Fix running e2e backend (#2710)

* Add DB environment variables to e2e.yaml and add csrf_decorate function

* Skip sonar suggestion

* Fix rest api internal error

* Add timeout

* Update docs

* Update code

* Revert csrf update

* Add command to dump local data

* Update dump and load data

* Update rest api config and docs

* Apply check-spelling

* Use .env.e2e.example for frontend e2e tests in gh actions

* Apply rabbit's suggestions

* Migrate dump_data to django command and dump owasp, github, and slack tables only

* Apply rabbit's suggestions

* Update code

* Refactor dump_data

* Use local cache for e2e

* Remove old load_data command

* Add tests

* Skip sonar

* Apply rabbit suggestions

* Update code

---------

Co-authored-by: Arkadii Yakovets <[email protected]>

* Add backend/data/nest.sql.gz

* Update code

* Automate data loading

* Update dump_data to avoid exceptions

* Update dump_data and automated data_loading

* Update CI/CD

* Update tests

* Add timeout for fuzz tests

* Update timeout for fuzz

* Update CI/CD

* Update CI/CD

* Update CI/CD

* Apply rabbit's suggestions

* Update backend/Makefile

* Update make targets and docker compose

* Add volume for graphql fuzzing results and add upload artifacts in ci/cd

* Update ci/cd

* Update ci/cd

* Update ci/cd

* Update ci/cd

* Update docker compose and makefile

* Apply rabbit's suggestions

* Update dump to match the last nest.json.gz

---------

Co-authored-by: Arkadii Yakovets <[email protected]>
* Add redis cache locally and in ci/cd

* Update env and makefile

* Update make target

* Add cache to fuzz and apply rabbit suggestions

* Update makefile target

* Add cache as dependency to the backend in docker compose

* Update file naming to match the remaining compose projects
* Update volumes

* Update compose folders

Update docker compose, dump file and ci/cd

Update ci/cd
* Migrate to schemathesis and add rest fuzz tests

* Apply cspell

* Update Fuzz Dockerfile context

* Update rest auth for fuzz

* Optimize Project Health Stats query and split rest and graphql tests

* Split rest and graphql tests workflows

* Update ci/cd

* Apply rabbit suggestions

* Update ci/cd

* Apply rabbit's suggestions

* Increase number of examples

* Apply rabbit's suggestions

* Apply pre-commit checks

* Update CI/CD

* Update makefile

* Update CI/CD

* Update CI/CD

* Update ci/cd

* Update ci/cd

* Update CI/CD

* Update settings.base.py and ci/cd

* Update configuration and ci/cd

* Update alphabitical order in env files

* Fix negative indexing

* Add EscapeNullCharactersMiddleware

* Update middleware, schemathesis config and add HTTP BadRequest status code to the rest api docs

* Update rest api schema

* Update tests

* Optimize recentIssues

* Add optimiazations and fixes

* Update tests, ci/cd and apply rabbit suggestions

* Optimize N+1 queries

* Update tests

* Update rest schema and add a name for Fuzz Tests job in CI/CD

* Fix negative indexing

* Update project health metrics filters and pagination

* Update mentorship app, ci/cd, and entrypoint

* Add trigram index to project

* Update nest.dump

* Update entrypoint

* Apply checks

* Add
QueryDepthLimiter

* Add optimizations

* Update tests

* Update CI/CD

* Add fixes

* Apply rabbit's suggestion

* Refactor docker files

* Apply cspell

* Refactor limits

* Update milestone enum and rest api endpoints

* Apply middleware suggestions

* Migrate to strawberry_django.field

* Update REDIS_AUTH_ENABLED default value

* Update queries to use strawberry_django.field

* Apply rabbit suggestions

* Update tests and appply rabbit suggestion

* Fix pagination.limit

* Add optimizations and fixes

* Update code

* Add optimizations

* Add optimizations

* Add optimizations

* Add fixes

* Add milestone index migration

* Update nest.dump

* Add optimizations and fixes

* Update snapshot query

* Update backend tests

* Update project model

* Apply rabbit suggestion

* Apply rabbit suggestions

* Apply rabbit suggestion and update tests

* Update code

---------

Co-authored-by: Arkadii Yakovets <[email protected]>
@github-actions github-actions bot added docs Improvements or additions to documentation backend frontend docker Pull requests that update Docker code backend-tests makefile ci labels Jan 17, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 17, 2026

Summary by CodeRabbit

  • New Features

    • Standardized validation error responses across REST API endpoints, providing clearer error messaging to clients.
  • Improvements

    • Optimized GraphQL query performance through improved data loading efficiency.
    • Enhanced platform security with middleware to filter requests containing null characters.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

This PR introduces comprehensive end-to-end (E2E) and fuzz testing infrastructure while refactoring GraphQL resolvers to use strawberry_django decorators with root-based field resolution. It adds environment-specific Django settings, CI/CD workflows, Docker Compose configurations, new data management commands, database indexes, and corresponding test suites across the backend and frontend.

Changes

Cohort / File(s) Summary
CI/CD Workflows
.github/workflows/run-ci-cd.yaml, .github/workflows/run-fuzz-tests.yaml, .github/workflows/setup-backend-environment/action.yaml, .github/workflows/update-nest-test-images.yaml
Added GitHub Actions workflows for E2E and fuzz testing with PostgreSQL/Redis services, backend setup composable action, and Docker image build steps. Expanded frontend E2E test job to provision services and load data.
Backend Environment Configuration
backend/settings/base.py, backend/settings/e2e.py, backend/settings/fuzz.py
Introduced E2E and Fuzz configuration classes with environment-specific settings, CORS/CSRF configuration, and Redis auth toggle. Added middleware for null character blocking.
GraphQL Node Refactoring
backend/apps/github/api/internal/nodes/issue.py, milestone.py, organization.py, pull_request.py, release.py, repository.py, user.py
Migrated field resolvers from strawberry.field to strawberry_django.field with root-based parameters, optimized related data loading, and added new public fields (assignees, author, organization on multiple nodes).
GraphQL Query Refactoring
backend/apps/github/api/internal/queries/issue.py, milestone.py, pull_request.py, release.py, repository.py, repository_contributor.py, user.py
Replaced OuterRef/Subquery distinct logic with Window/Rank-based approach, added MAX_LIMIT constants, enhanced select_related/prefetch_related for ORM optimization, and updated milestone state handling to enum-based.
OWASP GraphQL Nodes
backend/apps/owasp/api/internal/nodes/board_of_directors.py, chapter.py, committee.py, common.py, member_snapshot.py, project.py, project_health_metrics.py, snapshot.py
Converted resolvers to strawberry_django.field with root parameters, enabled query optimization hints, added root-based data traversal, and refactored snapshot inheritance from GenericEntityNode to strawberry.relay.Node.
OWASP GraphQL Queries
backend/apps/owasp/api/internal/queries/board_of_directors.py, chapter.py, committee.py, event.py, member_snapshot.py, post.py, project.py, project_health_metrics.py, snapshot.py, sponsor.py
Updated decorators to strawberry_django.field, added limit capping via MAX_LIMIT constants, implemented input validation for search queries, enhanced pagination constraint handling.
REST API Configuration
backend/apps/api/rest/v0/__init__.py, chapter.py, committee.py, common.py, event.py, issue.py, member.py, milestone.py, organization.py, project.py, release.py, repository.py, snapshot.py, sponsor.py
Added ValidationErrorSchema, global validation exception handler, and BAD_REQUEST response mapping across all GET endpoints. Updated EventBase date fields from datetime to ISO8601 strings with resolvers.
Django Models & Optimizations
backend/apps/github/models/milestone.py, repository.py, repository_contributor.py, backend/apps/owasp/models/chapter.py, committee.py, common.py, project.py, project_health_metrics.py, snapshot.py
Added database indexes (GIN for trigram search, descending for sorting), migrated from IndexBase lookups to ORM filters, optimized queries with select_related/prefetch_related, switched from QuerySet to cached_property for entity_leaders, refactored health metrics aggregation logic.
Django Migrations
backend/apps/github/migrations/0041_milestone_*.py, backend/apps/owasp/migrations/0070_snapshot_*.py, 0071_trigram_extension.py, 0072_project_*.py
Added indexes on milestone (created_at, updated_at), snapshot (created_at, key+status), enabled PostgreSQL trigram extension, and added GIN indexes with trigram operators on project name and leaders.
Management Commands & Data Handling
backend/apps/common/management/commands/dump_data.py, load_data.py
Introduced dump_data command with email redaction and temporary database usage; removed legacy load_data command in favor of pg_restore. Updated Makefile with load-data-e2e/fuzz variants and test-fuzz target.
Docker & Compose
docker/backend/Dockerfile, Dockerfile.fuzz, Dockerfile.local, Dockerfile.test, docker-compose/e2e/compose.yaml, docker-compose/fuzz/compose.yaml, docker-compose/production/compose.yaml, docker-compose/staging/compose.yaml
Added Dockerfile.fuzz for fuzz testing, new docker-compose files for E2E and fuzz environments with multi-service orchestration (backend, data-loader, db, cache, test containers), adjusted base Dockerfile entrypoint handling, updated PostgreSQL client package reference.
Backend Entrypoints & Configuration
backend/entrypoint.fuzz.sh, backend/.env.e2e.example, backend/.env.fuzz.example, backend/.env.example, backend/pyproject.toml
Added fuzz entrypoint script with CSRF token handling and Schemathesis configuration, new E2E/fuzz environment variable examples, Redis auth toggle, and Ruff lint ignore rule for subprocess security checks.
Test Updates - Backend Nodes
backend/tests/apps/common/graphql_node_base_test.py, backend/tests/apps/github/api/internal/nodes/issue_test.py, milestone_test.py, organization_test.py, pull_request_test.py, release_test.py, repository_test.py, user_test.py, backend/tests/apps/owasp/api/internal/nodes/*.py
Introduced GraphQLNodeBaseTest base class with _get_field_by_name helper, migrated all node tests to inherit from it, refactored resolver invocations to use field.base_resolver.wrapped_func pattern.
Test Updates - Backend Queries
backend/tests/apps/github/api/internal/queries/issue_test.py, milestone_test.py, pull_request_test.py, release_test.py, repository_test.py, backend/tests/apps/owasp/api/internal/queries/member_snapshot_test.py
Replaced select_related/Subquery mocking with annotate/filter/exclude patterns, updated distinct-filtering tests to use Window/Rank approach, removed OuterRef/Subquery patches, added eager-loading assertions (select_related/prefetch_related).
Test Updates - Models & Commands
backend/tests/apps/github/models/repository_contributor_test.py, repository_test.py, backend/tests/apps/owasp/models/chapter_test.py, committee_test.py, backend/tests/apps/common/management/commands/dump_data_test.py, load_data_test.py, backend/tests/apps/api/rest/v0/event_test.py
Added dump_data command tests with mocked DB operations, removed load_data command tests, refactored model tests to use ORM filters instead of IndexBase mocks, updated event serializer tests with ORM instances.
Fuzz Testing Modules
backend/tests/fuzz/graphql_test.py, rest_test.py
Added Schemathesis-based fuzz test modules for GraphQL and REST APIs with environment variable validation, header configuration, and case-level validation with error suppression.
Frontend & Configuration
frontend/.env.e2e.example, frontend/Makefile, frontend/src/server/queries/homeQueries.ts, .github/workflows/run-ci-cd.yaml (frontend test updates), .gitignore, CONTRIBUTING.md, cspell/custom-dict.txt
Added E2E environment example, refactored test-frontend-e2e to use docker-compose orchestration, removed milestone state filter from GraphQL query, updated documentation with E2E/fuzz sections, added dictionary entries for new tools (schemathesis, trgm, pgpassword).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

backend, frontend, docker, backend-tests, ci, graphql, testing

Suggested reviewers

  • kasya
  • arkid15r
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive The description is a template with placeholder text and incomplete checklist, providing minimal details about the actual changes or linked issue. Fill in the description with specific details about changes, add a linked issue number, and complete the checklist items to clarify the PR scope and verification status.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: implementing e2e backend instance and fuzz testing infrastructure for both GraphQL and REST endpoints.
Docstring Coverage ✅ Passed Docstring coverage is 94.44% 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
  • Commit unit tests in branch feature/e2e-backend

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.

@github-actions
Copy link

PR validation failed: No linked issue and no valid closing issue reference in PR description

@github-actions github-actions bot closed this Jan 17, 2026
@ahmedxgouda ahmedxgouda reopened this Jan 17, 2026
@ahmedxgouda ahmedxgouda marked this pull request as ready for review January 17, 2026 04:32
@sonarqubecloud
Copy link

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: 17

Caution

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

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

52-52: Docstring doesn't match implementation.

The docstring states "unique pull requests per author and repository," but the implementation at lines 94-96 only partitions by author_id. Either update the docstring to reflect "unique pull requests per author" or update the partition_by to include repository_id.

📝 Proposed docstring fix
-            distinct (bool): Whether to return unique pull requests per author and repository.
+            distinct (bool): Whether to return unique pull requests per author.

Or if the original intent was to partition by both:

                     partition_by=[F("author_id")],
+                    partition_by=[F("author_id"), F("repository_id")],
backend/apps/owasp/api/internal/queries/member_snapshot.py (1)

48-74: Remove decorator hints from member_snapshots method.

The @strawberry_django.field decorator should not use select_related or prefetch_related parameters for methods returning relationship objects (list[MemberSnapshotNode]). Per strawberry-graphql-django conventions, apply these optimizations inside the resolver instead. The current decorator hints also reference nested relationships (github_user__owasp_profile, user_badges__badge) that are not exposed by MemberSnapshotNode's schema, making them unnecessary. Follow the pattern used by the singular member_snapshot method: remove the decorator parameters and rely on the in-resolver optimizations.

♻️ Suggested fix
-    `@strawberry_django.field`(
-        select_related=["github_user__owasp_profile", "github_user__user_badges__badge"],
-        prefetch_related=["issues", "pull_requests", "messages"],
-    )
+    `@strawberry_django.field`
     def member_snapshots(
         self, user_login: str | None = None, limit: int = 10
     ) -> list[MemberSnapshotNode]:
backend/apps/owasp/api/internal/queries/project.py (1)

93-104: Avoid icontains("") which matches everything.

If github_user.name is None (or empty), leaders_raw__icontains="" will match all projects, making is_project_leader return True for any existing project. That’s a correctness bug and could grant unintended access.

🐛 Proposed fix
-        return Project.objects.filter(
-            Q(leaders_raw__icontains=github_user.login)
-            | Q(leaders_raw__icontains=(github_user.name or ""))
-        ).exists()
+        filters = Q(leaders_raw__icontains=github_user.login)
+        if github_user.name:
+            filters |= Q(leaders_raw__icontains=github_user.name)
+        return Project.objects.filter(filters).exists()
🤖 Fix all issues with AI agents
In `@backend/apps/common/management/commands/dump_data.py`:
- Around line 87-93: The finally block is catching CalledProcessError but
_execute_sql raises psycopg2 exceptions; change the exception handler to catch
psycopg2.Error (or psycopg2.DatabaseError) instead of CalledProcessError, and
log the exception details (e.g., include the exception message variable) in the
warning so failures in _execute_sql within dump_data.py are actually reported;
reference the _execute_sql call and replace the Except CalledProcessError branch
with Except psycopg2.Error as e and include e in the self.stderr.write warning.
- Around line 54-56: The CREATE DATABASE call in dump_data.py currently
interpolates temp_db and DB_NAME via f-strings in self._execute_sql which is
unsafe; update the code that builds the SQL for CREATE DATABASE (the call using
self._execute_sql("postgres", [...])) to construct the statement with
psycopg2.sql.SQL and psycopg2.sql.Identifier (e.g., SQL("CREATE DATABASE {}
TEMPLATE {}").format(Identifier(temp_db), Identifier(DB_NAME))) so identifiers
are properly quoted/escaped before passing to _execute_sql; ensure any other
places that interpolate database names (temp_db, DB_NAME) use the same
sql.Identifier approach.

In `@backend/apps/github/api/internal/nodes/pull_request.py`:
- Around line 38-41: In the url resolver method (def url(self, root:
PullRequest) -> str) remove the redundant fallback and return root.url directly
(i.e., replace "return root.url or \"\"" with "return root.url") so the
PullRequest url field matches other node resolvers like
RepositoryNode/UserNode/ReleaseNode/OrganizationNode that return root.url
without an empty-string fallback.

In `@backend/apps/github/api/internal/queries/issue.py`:
- Around line 72-81: The distinct logic in the
queryset.annotate(rank=Window(...)) currently partitions only by F("author_id")
so it returns one issue per author globally; update the partition_by argument in
the Window expression used in this block (the Rank() window annotation on
queryset in the distinct branch) to include F("repository_id") as well (i.e.,
partition_by=[F("author_id"), F("repository_id")]) so uniqueness is enforced per
author-and-repository as the docstring promises.

In `@backend/apps/github/api/internal/queries/pull_request.py`:
- Around line 91-102: The unit tests for the distinct=True branch in
pull_request_test.py use MagicMock and only assert
annotate()/filter()/order_by() call counts, so they don't validate actual SQL or
DB behavior; add an integration test that runs the distinct branch against a
real test database (using Django TestCase or TransactionTestCase) by invoking
the query construction in
backend/apps/github/api/internal/queries/pull_request.py (the function that
builds the queryset for pull requests) and asserting the returned rows and/or
generated SQL respect the windowed rank filtering (e.g., only the latest per
author), ensuring the Window(Rank()) annotate +
filter(rank=1).order_by("-created_at") path is executed against the DB rather
than mocked.

In `@backend/apps/github/api/internal/queries/release.py`:
- Around line 61-70: The window partition currently groups only by
F("author_id"), which contradicts the docstring that promises unique releases
per author and repository; update the distinct logic where queryset is annotated
with Window/Rank (the block referencing rank=Window(expression=Rank(),
partition_by=[F("author_id")], ...)) to include F("repository_id") in the
partition_by list so the partitioning becomes [F("author_id"),
F("repository_id")] before filtering rank=1.

In `@backend/apps/github/api/internal/queries/repository.py`:
- Around line 40-42: Remove the select_related hints from the
`@strawberry_django.field` decorator in repository.py and instead apply ORM
optimization inside the field's resolver (or rely on DjangoOptimizerExtension).
Concretely, delete the select_related argument from the decorator and in the
resolver for this field use queryset.select_related("organization",
"owner__owasp_profile").prefetch_related("owner__user_badges__badge") (or let
DjangoOptimizerExtension handle it) so reverse/many relationships are prefetched
correctly and Strawberry schema hints are not exposed.

In `@backend/apps/github/api/internal/queries/user.py`:
- Line 40: The decorator currently passes the reverse FK path to select_related
which cannot traverse reverse relations; update the `@strawberry_django.field`
call so that select_related only includes "owasp_profile" and add
prefetch_related with "user_badges__badge" (i.e., use
select_related=["owasp_profile"] and prefetch_related=["user_badges__badge"]) to
correctly prefetch the reverse user_badges relation while keeping owasp_profile
as a select_related.

In `@backend/apps/owasp/api/internal/nodes/committee.py`:
- Around line 18-22: The created_at resolver (def created_at(self, root:
Committee) -> float) can return None via root.idx_created_at which contradicts
its non-nullable float signature; add a guard that returns a default float
(e.g., 0.0) when root.idx_created_at is falsy/None, mirroring the pattern used
by contributors_count/forks_count—ensure the resolver always returns a float
value.

In `@backend/apps/owasp/api/internal/permissions/project_health_metrics.py`:
- Around line 12-22: In has_permission, guard against user.github_user being
None to avoid AttributeError; update the final condition in has_permission to
verify github_user exists before accessing is_owasp_staff (for example check
user.github_user is not None or use getattr(user.github_user, "is_owasp_staff",
False)) so the method returns False when github_user is missing while preserving
behavior in settings.IS_E2E_ENVIRONMENT/IS_FUZZ_ENVIRONMENT.

In `@backend/apps/owasp/api/internal/queries/post.py`:
- Around line 16-19: Update the recent_posts resolver docstring to reflect the
behavior: mention that it returns up to `limit` most recent posts (default 5)
and that `limit` is capped by `MAX_LIMIT`; locate the docstring on the
`recent_posts` method (decorated with `@strawberry_django.field`) which calls
`Post.recent_posts()` and refers to `MAX_LIMIT` and adjust the text so it no
longer claims a fixed "5" posts.

In `@backend/apps/owasp/api/internal/views/permissions.py`:
- Around line 11-15: Add a startup assertion inside the Production configuration
class to prevent accidentally enabling E2E/fuzz auth bypass in production: in
the Production config (the settings class used for production) override __init__
(call super().__init__(*args, **kwargs)) and then assert not
self.IS_E2E_ENVIRONMENT and not self.IS_FUZZ_ENVIRONMENT (or raise RuntimeError
with a clear message) so startup fails if those flags are true; reference the
Production config class and the IS_E2E_ENVIRONMENT / IS_FUZZ_ENVIRONMENT
attributes when making this change.

In `@backend/apps/owasp/models/project_health_metrics.py`:
- Around line 149-152: The property project_requirements currently calls
ProjectHealthRequirements.objects.get(level=self.project.level) which will raise
if no record exists; change it to a safe lookup that returns None when missing
(e.g., use
ProjectHealthRequirements.objects.filter(level=self.project.level).first() or
catch ProjectHealthRequirements.DoesNotExist and return None) so the annotated
return type ProjectHealthRequirements | None is honored and the other accessors
can continue to check if self.project_requirements before use.

In `@backend/entrypoint.fuzz.sh`:
- Around line 10-49: The curl call that sets CSRF_TOKEN should include connect
and overall timeouts to avoid hanging in CI and fail fast on network issues
(e.g., add appropriate --connect-timeout and --max-time options to the curl
invocation that produces CSRF_TOKEN); also protect the pytest invocations from
word-splitting/globbing by quoting the TEST_FILE expansion in both branches
(wrap the ./tests/${TEST_FILE} operand in quotes) so filenames with spaces or
special chars are handled safely.

In `@backend/pyproject.toml`:
- Around line 105-108: Update the per-file ruff ignores for
lint.per-file-ignores."**/management/commands/dump_data.py": remove the
unnecessary "S603" entry (since dump_data.py does not use shell=True) and keep
only "S607", or if you prefer to keep S603 for future caution, add an inline
comment next to the ignore explaining why it's retained; reference the ignore
list key "lint.per-file-ignores.\"**/management/commands/dump_data.py\"" and the
rule IDs "S603" and "S607" when making the change so reviewers can see you
either narrowed the suppression to S607 or documented the justification for
S603.

In `@docker-compose/e2e/compose.yaml`:
- Around line 22-28: The healthcheck for the backend service uses wget but the
backend image (python:3.13.7-alpine) doesn't include it, causing the check to
always fail; either install wget in the backend Dockerfile by adding a package
install (e.g., apk add --no-cache wget) so the healthcheck's test: wget --spider
http://backend:9000/a/ succeeds, or replace the healthcheck block
(healthcheck/test) with a Python-based check that performs an HTTP GET to
http://backend:9000/a/ (using python -c 'import sys,urllib.request;
urllib.request.urlopen("http://backend:9000/a/")' and exiting nonzero on
failure) so the backend service can become healthy.

In `@frontend/.env.e2e.example`:
- Around line 1-16: Reorder the env keys so NEXTAUTH_* entries appear before
NEXT_PUBLIC_* to satisfy dotenv-linter ordering: move NEXTAUTH_SECRET and
NEXTAUTH_URL to above the NEXT_PUBLIC_API_URL (or above the first NEXT_PUBLIC_*
block), keeping all other keys unchanged; ensure the file still contains
NEXT_SERVER_* entries and secrets unchanged, or alternatively add a
dotenv-linter exemption comment if you prefer to keep the current order.
🧹 Nitpick comments (34)
backend/apps/mentorship/api/internal/queries/mentorship.py (2)

53-88: Approve changes with a minor consistency suggestion.

The refactor to return None instead of raising exceptions is appropriate. However, consider adding exc_info=True to the exception handler at lines 85-88 for consistency with program.py and module.py, which include the traceback for debugging.

Optional: Add exc_info for consistency
         except (Module.DoesNotExist, GithubUser.DoesNotExist, Mentee.DoesNotExist) as e:
             message = f"Mentee details not found: {e}"
-            logger.warning(message)
+            logger.warning(message, exc_info=True)
             return None

129-132: Same consistency suggestion for this exception handler.

Consider adding exc_info=True here as well.

Optional: Add exc_info for consistency
         except (Module.DoesNotExist, GithubUser.DoesNotExist, Mentee.DoesNotExist) as e:
             message = f"Mentee issues not found: {e}"
-            logger.warning(message)
+            logger.warning(message, exc_info=True)
             return []
backend/apps/api/rest/v0/organization.py (1)

82-82: Minor documentation inconsistency (pre-existing).

The endpoint description (line 82) says "Retrieve project details" and the docstring (line 96) says "Get project", but this endpoint handles organizations. Consider fixing for clarity.

📝 Suggested fix
 `@router.get`(
     "/{str:organization_id}",
-    description="Retrieve project details.",
+    description="Retrieve organization details.",
     operation_id="get_organization",
 def get_organization(
     request: HttpRequest,
     organization_id: str = Path(example="OWASP"),
 ) -> OrganizationDetail | OrganizationError:
-    """Get project."""
+    """Get organization."""

Also applies to: 96-96

backend/apps/api/rest/v0/__init__.py (2)

61-82: E2E and Fuzz settings are nearly identical.

These two blocks differ only in the "description" string. Consider consolidating them:

elif settings.IS_E2E_ENVIRONMENT or settings.IS_FUZZ_ENVIRONMENT:
    env_name = "E2E" if settings.IS_E2E_ENVIRONMENT else "Fuzz"
    api_settings_customization = {
        "auth": None,
        "servers": [{"description": env_name, "url": settings.SITE_URL}],
        "throttle": [],
    }

That said, keeping them separate follows the existing pattern (e.g., IS_LOCAL_ENVIRONMENT) and allows for independent future changes—so this is purely optional.


107-114: Add type hints to the exception handler.

The handler is missing type annotations for better maintainability and IDE support.

♻️ Proposed fix
+from django.http import HttpRequest, HttpResponse
+
 `@api.exception_handler`(ValidationError)
-def validation_exception_handler(request, exc):
+def validation_exception_handler(request: HttpRequest, exc: ValidationError) -> HttpResponse:
     """Handle validation exceptions."""
     return api.create_response(
         request,
         {"message": "Invalid request", "errors": exc.errors},
         status=400,
     )
backend/tests/apps/api/rest/v0/event_test.py (1)

4-9: Simplify date construction; timezone lookup is redundant.

Line 9 captures the current timezone, but the value is immediately discarded by .date() in Lines 17, 22, and 32. Using date(...) directly is simpler and avoids the global timezone dependency.

♻️ Proposed simplification
-from datetime import datetime
+from datetime import date

-import pytest
-from django.utils import timezone
+import pytest

-from apps.api.rest.v0.event import EventDetail
-from apps.owasp.models.event import Event as EventModel
-
-current_timezone = timezone.get_current_timezone()
+from apps.api.rest.v0.event import EventDetail
+from apps.owasp.models.event import Event as EventModel
@@
-        EventModel(
+        EventModel(
             description="this is a sample event",
-            end_date=datetime(2023, 6, 15, tzinfo=current_timezone).date(),
+            end_date=date(2023, 6, 15),
             key="sample-event",
             latitude=59.9139,
             longitude=10.7522,
             name="sample event",
-            start_date=datetime(2023, 6, 14, tzinfo=current_timezone).date(),
+            start_date=date(2023, 6, 14),
             url="https://github.com/owasp/Nest",
         ),
@@
-        EventModel(
+        EventModel(
             description=None,
             end_date=None,
             key="event-without-end-date",
             latitude=None,
             longitude=None,
             name="event without end date",
-            start_date=datetime(2023, 7, 1, tzinfo=current_timezone).date(),
+            start_date=date(2023, 7, 1),
             url=None,
         ),

Also applies to: 15-33

backend/apps/owasp/migrations/0071_trigram_extension.py (1)

7-13: Verify pg_trgm availability in target DBs.
CREATE EXTENSION requires appropriate privileges; please confirm CI/staging/prod roles allow this migration.

.github/workflows/update-nest-test-images.yaml (1)

77-89: Add GHA cache-from for fuzz image builds.

Other image build steps use both GHA and registry caches; this step only pulls registry cache, so the GHA cache you push won’t be reused. Adding type=gha keeps caching consistent and speeds CI.

♻️ Proposed tweak
-          cache-from: type=registry,ref=owasp/nest:test-fuzz-backend-cache
+          cache-from: |
+            type=gha
+            type=registry,ref=owasp/nest:test-fuzz-backend-cache
frontend/Makefile (1)

82-88: Clear Redis volume to avoid cross-run cache bleed.

Only the DB volume is removed; the cache volume persists and can leak state between runs. Consider clearing nest-e2e_e2e-cache-data as well.

♻️ Suggested cleanup
 test-frontend-e2e:
 	`@docker` container rm -f e2e-nest-db >/dev/null 2>&1 || true
 	`@docker` volume rm -f nest-e2e_e2e-db-data >/dev/null 2>&1 || true
+	`@docker` volume rm -f nest-e2e_e2e-cache-data >/dev/null 2>&1 || true
 	`@DOCKER_BUILDKIT`=1 \
 	docker compose --project-name nest-e2e -f docker-compose/e2e/compose.yaml up --build --remove-orphans --abort-on-container-exit db cache backend data-loader
docker/backend/Dockerfile.fuzz (1)

26-29: Redundant chmod +x after COPY with --chmod=755.

Line 26 already sets executable permissions via --chmod=755, making line 29 unnecessary.

🔧 Suggested fix
 COPY --chown=root:root --chmod=755 ./entrypoint.fuzz.sh ./entrypoint.sh
 COPY --chown=root:root --chmod=755 tests/fuzz tests
 
-RUN chmod +x ./entrypoint.sh
-
 USER owasp
backend/tests/fuzz/rest_test.py (1)

26-30: Add type hint for consistency with GraphQL test.

The case parameter lacks a type hint, unlike test_graphql_api which uses case: schemathesis.Case.

🔧 Suggested fix
 `@schema.parametrize`()
-def test_rest_api(case):
+def test_rest_api(case: schemathesis.Case) -> None:
     """Test REST API endpoints."""
     logger.info(case.as_curl_command())
     case.call_and_validate(headers=HEADERS)
backend/tests/fuzz/graphql_test.py (1)

32-33: Consider narrowing the scope of suppressed errors or logging them.

Blanket suppression of GraphQLClientError may hide unexpected issues during fuzz testing. Consider logging suppressed errors at DEBUG level for traceability.

🔧 Suggested enhancement
+from schemathesis.graphql.checks import GraphQLClientError
+
 `@schema.parametrize`()
 def test_graphql_api(case: schemathesis.Case) -> None:
     """Test GraphQL API endpoints."""
     logger.info(case.as_curl_command())
-    with suppress(GraphQLClientError):
-        case.call_and_validate(headers=HEADERS)
+    try:
+        case.call_and_validate(headers=HEADERS)
+    except GraphQLClientError as e:
+        logger.debug("GraphQL client error (expected): %s", e)
docker-compose/fuzz/compose.yaml (1)

85-85: Redis password exposed in process list via -a flag.

The redis-cli -a $$REDIS_PASSWORD ping command exposes the password in the process list. For a fuzz testing environment this is likely acceptable, but consider using --pass from a file or REDISCLI_AUTH environment variable for production-like setups.

.github/workflows/run-ci-cd.yaml (1)

288-301: Redundant port mapping with --network host.

The -p 9000:9000 flag is ignored when using --network host since host networking doesn't use Docker's port mapping. While harmless, removing it would reduce confusion.

♻️ Suggested fix
       - name: Start Backend in the background
         run: |
           docker run -d --rm --name e2e-nest-backend \
             --env-file backend/.env.e2e.example \
             --network host \
             -e DJANGO_DB_HOST=localhost \
             -e DJANGO_REDIS_AUTH_ENABLED=False \
             -e DJANGO_REDIS_HOST=localhost \
-            -p 9000:9000 \
             owasp/nest:test-backend-latest \
             sh -c '
               python manage.py migrate &&
               gunicorn wsgi:application --bind 0.0.0.0:9000
           '
.github/workflows/run-fuzz-tests.yaml (1)

57-70: Redundant port mapping with --network host.

Same as the E2E workflow, the -p 9500:9500 flag is ignored when using --network host.

♻️ Suggested fix
       - name: Run backend with fuzz environment variables
         run: |
           docker run -d --rm --name fuzz-nest-backend \
             --env-file backend/.env.fuzz.example \
             --network host \
             -e DJANGO_DB_HOST=localhost \
             -e DJANGO_REDIS_AUTH_ENABLED=False \
             -e DJANGO_REDIS_HOST=localhost \
-            -p 9500:9500 \
             owasp/nest:test-backend-latest \
             sh -c '
               python manage.py migrate &&
               gunicorn wsgi:application --bind 0.0.0.0:9500
             '
backend/apps/common/management/commands/dump_data.py (2)

102-106: Type hint mismatch: method returns sql.Composed objects, not str.

sql.SQL(...).format(...) returns sql.Composed objects. The return type hint list[str] is incorrect and may confuse static analyzers.

✏️ Suggested fix
+from psycopg2.sql import Composed
+
-    def _remove_emails(self, tables: list[str]) -> list[str]:
+    def _remove_emails(self, tables: list[str]) -> list[Composed]:
         return [
             sql.SQL("UPDATE {table} SET email = '';").format(table=sql.Identifier(table))
             for table in tables
         ]

108-130: Connection may leak on exception; use context manager.

If an exception occurs during query execution, connection.close() is never called. Use the connection as a context manager or wrap in try/finally.

Additionally, the sql_queries parameter accepts both str and sql.Composed objects but the type hint only specifies str.

♻️ Proposed fix
+from typing import Union
+from psycopg2.sql import Composable
+
     def _execute_sql(
         self,
         dbname: str,
-        sql_queries: list[str],
+        sql_queries: list[Union[str, Composable]],
     ):
-        connection = connect(
+        with connect(
             dbname=dbname,
             user=DB_USER,
             password=DB_PASSWORD,
             host=DB_HOST,
             port=DB_PORT,
-        )
-        connection.autocommit = True
-
-        rows = []
-        with connection.cursor() as cursor:
-            for sql_query in sql_queries:
-                cursor.execute(sql_query)
-                with contextlib.suppress(ProgrammingError):
-                    rows.extend(cursor.fetchall())
-        connection.close()
-
-        return rows
+        ) as connection:
+            connection.autocommit = True
+            rows = []
+            with connection.cursor() as cursor:
+                for sql_query in sql_queries:
+                    cursor.execute(sql_query)
+                    with contextlib.suppress(ProgrammingError):
+                        rows.extend(cursor.fetchall())
+            return rows
backend/Makefile (1)

124-125: Consider validating APP_NAME is set.

If APP_NAME is not provided, the command will run with an empty argument, potentially causing a confusing Django error. Consider adding validation or a usage hint.

✏️ Optional enhancement
 migrations-empty:
+ifndef APP_NAME
+	$(error APP_NAME is required. Usage: make migrations-empty APP_NAME=<app_name>)
+endif
 	`@CMD`="python manage.py makemigrations --empty $(APP_NAME)" $(MAKE) exec-backend-command
backend/apps/github/models/repository.py (1)

175-177: Keep recent_milestones return type consistent.
Returning [] changes the type from QuerySet to list, which can break callers that chain queryset methods. Consider returning an empty QuerySet instead.

♻️ Proposed adjustment
-        return self.milestones.order_by("-created_at") if self.pk else []
+        if not self.pk:
+            return self.milestones.model.objects.none()
+        return self.milestones.order_by("-created_at")
backend/tests/apps/owasp/models/committee_test.py (1)

28-33: Consider clearing the lru_cache to ensure test isolation.

The active_committees_count method uses @lru_cache, which persists across test runs in the same process. If multiple parameterized values are tested or other tests call this method first, the cached result may be returned instead of the mocked value.

♻️ Suggested fix to clear the cache
     `@pytest.mark.parametrize`(
         ("value"),
         [
             23,
         ],
     )
     def test_active_committees_count(self, value):
+        Committee.active_committees_count.cache_clear()
         with patch.object(Committee.objects, "filter") as mock_filter:
             mock_filter.return_value.count.return_value = value
             count = Committee.active_committees_count()
             assert count == value
             mock_filter.assert_called_once_with(has_active_repositories=True)
backend/tests/apps/owasp/models/chapter_test.py (1)

109-124: Consider clearing lru_cache between tests.

The active_chapters_count method uses @lru_cache, which persists across test runs within the same process. This could cause test pollution if other tests call this method.

Consider adding cache clearing in test setup or using Chapter.active_chapters_count.cache_clear() to ensure test isolation.

♻️ Suggested fix
     `@pytest.mark.parametrize`(
         ("value"),
         [
             42,
         ],
     )
     def test_active_chapters_count(self, value):
+        Chapter.active_chapters_count.cache_clear()
         with patch.object(Chapter.objects, "filter") as mock_filter:
             mock_filter.return_value.count.return_value = value
             assert Chapter.active_chapters_count() == value
             mock_filter.assert_called_once_with(
                 is_active=True,
                 latitude__isnull=False,
                 longitude__isnull=False,
                 owasp_repository__is_empty=False,
             )
backend/tests/apps/owasp/api/internal/queries/member_snapshot_test.py (1)

35-50: Tighten eager‑loading assertions to lock in the intended relations.

Right now the test only checks call counts. Asserting the exact select_related/prefetch_related args (and filter args) makes regressions easier to catch.

♻️ Suggested test hardening
-            # select_related and prefetch_related are called, then filter twice
-            mock_queryset.select_related.assert_called_once()
-            mock_queryset.prefetch_related.assert_called_once()
-            assert mock_queryset.filter.call_count == 2
+            # select_related and prefetch_related are called, then filter twice
+            mock_queryset.select_related.assert_called_once_with("github_user")
+            mock_queryset.prefetch_related.assert_called_once_with(
+                "issues", "pull_requests", "messages"
+            )
+            mock_queryset.filter.assert_any_call(github_user=mock_user)
+            mock_queryset.filter.assert_any_call(start_at__year=2025)
backend/tests/apps/owasp/api/internal/nodes/chapter_test.py (1)

77-78: Add an explicit null-guard before invoking the resolver.

This makes failures clearer if the field lookup ever changes.

💡 Suggested tweak
 field = self._get_field_by_name("contribution_stats", ChapterNode)
+assert field is not None
 result = field.base_resolver.wrapped_func(None, instance)
backend/apps/owasp/models/project.py (1)

222-225: Consider prefetch-friendly access for last_health_metrics.

Ordering on the related manager issues a query per project and bypasses any prefetch cache; if this property is used in lists, consider a Prefetch(..., to_attr=...) or an annotation-based approach for the latest metric.

backend/tests/apps/owasp/api/internal/nodes/member_snapshot_test.py (1)

35-36: Guard against missing field lookups to avoid AttributeError.

_get_field_by_name can return None, which would make field.base_resolver crash with a less clear failure. Consider asserting the field exists (or updating the helper to raise a clearer error) before invocation.

🔧 Suggested tweak (apply similarly to each resolver test)
-        field = self._get_field_by_name("commits_count", MemberSnapshotNode)
-        result = field.base_resolver.wrapped_func(None, mock_snapshot)
+        field = self._get_field_by_name("commits_count", MemberSnapshotNode)
+        assert field is not None
+        result = field.base_resolver.wrapped_func(None, mock_snapshot)

Also applies to: 46-47, 56-57, 66-67, 76-77, 86-87

backend/tests/apps/github/api/internal/nodes/issue_test.py (1)

45-47: Add a guard for missing fields to improve test failure clarity.

If _get_field_by_name returns None, the failure will be an attribute error instead of a clear “field not found.” Consider asserting the field exists before invoking base_resolver.

💡 Suggested tweak (apply to each lookup)
-        field = self._get_field_by_name("organization_name", IssueNode)
-        result = field.base_resolver.wrapped_func(None, mock_issue)
+        field = self._get_field_by_name("organization_name", IssueNode)
+        assert field is not None
+        result = field.base_resolver.wrapped_func(None, mock_issue)

Also applies to: 56-58, 65-67, 76-78, 85-87

backend/tests/apps/github/api/internal/nodes/pull_request_test.py (1)

39-41: Consider asserting field existence before calling the resolver.

This makes schema drift failures more readable than an attribute error.

💡 Suggested tweak (apply to each lookup)
-        field = self._get_field_by_name("organization_name", PullRequestNode)
-        result = field.base_resolver.wrapped_func(None, mock_pr)
+        field = self._get_field_by_name("organization_name", PullRequestNode)
+        assert field is not None
+        result = field.base_resolver.wrapped_func(None, mock_pr)

Also applies to: 50-52, 59-61, 70-72, 79-81, 88-90, 97-99

backend/apps/github/api/internal/nodes/organization.py (1)

42-71: Consider adding select_related or query optimization hints.

The stats resolver makes multiple database queries (repositories count, aggregate, and contributors count). While the aggregation logic is correct, the resolver doesn't leverage strawberry_django.field's query optimization hints like other resolvers in the PR do.

If this resolver is called frequently, consider whether any of the related data could benefit from caching or whether the queries could be combined. However, since the queries are already using aggregations efficiently, this is a minor optimization opportunity.

backend/apps/github/api/internal/queries/repository.py (1)

59-67: Consider simplifying the limit validation for readability.

The walrus operator with the ternary is functionally correct but somewhat dense. A more explicit approach could improve readability:

♻️ Optional refactor for clarity
-        return (
-            (
-                Repository.objects.filter(
-                    organization__login__iexact=organization,
-                ).order_by("-stars_count")[:limit]
-            )
-            if (limit := min(limit, MAX_LIMIT)) > 0
-            else []
-        )
+        limit = min(limit, MAX_LIMIT)
+        if limit <= 0:
+            return []
+        return list(
+            Repository.objects.filter(
+                organization__login__iexact=organization,
+            ).order_by("-stars_count")[:limit]
+        )
backend/apps/github/api/internal/nodes/issue.py (2)

48-51: Avoid per-issue queries in is_merged.

filter(...).exists() bypasses the prefetched collection and triggers a query per issue. Consider an in-memory check (or annotate at the queryset) to leverage prefetch. Please confirm query counts in tests.

♻️ Suggested update
-        return root.pull_requests.filter(state="closed", merged_at__isnull=False).exists()
+        return any(
+            pr.state == "closed" and pr.merged_at is not None
+            for pr in root.pull_requests.all()
+        )

53-61: Use prefetched participant_interests to avoid extra queries.

select_related().order_by() forces a new query, so the prefetch hint isn’t used. Sorting the prefetched list keeps it in-memory. Please confirm query counts in tests.

♻️ Suggested update
-        return [
-            interest.user
-            for interest in root.participant_interests.select_related("user").order_by(
-                "user__login"
-            )
-        ]
+        interests = sorted(
+            root.participant_interests.all(),
+            key=lambda interest: interest.user.login,
+        )
+        return [interest.user for interest in interests]
backend/apps/github/api/internal/nodes/repository.py (3)

46-51: Drop unused prefetch on issues.

order_by(...).[:limit] forces a fresh query per repository, so the prefetch is unused and can add redundant work. Consider removing it (or refactor to a prefetched to_attr list). Please confirm query counts in tests.

♻️ Suggested update
-    `@strawberry_django.field`(prefetch_related=["issues"])
+    `@strawberry_django.field`

69-72: Drop unused prefetch on recent_milestones.

The ordered slice triggers a new query per repository, so the prefetch isn’t used. Consider removing it (or refactor to use a prefetched to_attr list). Please confirm query counts in tests.

♻️ Suggested update
-    `@strawberry_django.field`(prefetch_related=["milestones"])
+    `@strawberry_django.field`

74-78: Drop unused prefetch on releases.

The ordered slice triggers a new query per repository, so the prefetch isn’t used. Consider removing it (or refactor to use a prefetched to_attr list). Please confirm query counts in tests.

♻️ Suggested update
-    `@strawberry_django.field`(prefetch_related=["releases"])
+    `@strawberry_django.field`

Comment on lines +54 to +56
try:
self._execute_sql("postgres", [f"CREATE DATABASE {temp_db} TEMPLATE {DB_NAME};"])

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

SQL injection risk: use sql.Identifier for database names.

Database names are interpolated directly into SQL via f-string. While DB_NAME comes from settings, this pattern is unsafe if the name contains special characters or is modified. Use psycopg2.sql module for safe identifier handling.

Note: CREATE DATABASE cannot use parameterized queries, but sql.Identifier properly escapes identifiers.

🔒 Proposed fix
-            self._execute_sql("postgres", [f"CREATE DATABASE {temp_db} TEMPLATE {DB_NAME};"])
+            create_db_sql = sql.SQL("CREATE DATABASE {} TEMPLATE {};").format(
+                sql.Identifier(temp_db),
+                sql.Identifier(DB_NAME)
+            )
+            self._execute_sql("postgres", [create_db_sql])
🤖 Prompt for AI Agents
In `@backend/apps/common/management/commands/dump_data.py` around lines 54 - 56,
The CREATE DATABASE call in dump_data.py currently interpolates temp_db and
DB_NAME via f-strings in self._execute_sql which is unsafe; update the code that
builds the SQL for CREATE DATABASE (the call using self._execute_sql("postgres",
[...])) to construct the statement with psycopg2.sql.SQL and
psycopg2.sql.Identifier (e.g., SQL("CREATE DATABASE {} TEMPLATE
{}").format(Identifier(temp_db), Identifier(DB_NAME))) so identifiers are
properly quoted/escaped before passing to _execute_sql; ensure any other places
that interpolate database names (temp_db, DB_NAME) use the same sql.Identifier
approach.

Comment on lines +87 to +93
finally:
try:
self._execute_sql("postgres", [f"DROP DATABASE IF EXISTS {temp_db};"])
except CalledProcessError:
self.stderr.write(
self.style.WARNING(f"Failed to drop temp DB {temp_db} (ignored).")
)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Wrong exception type in finally block.

_execute_sql raises psycopg2 exceptions (e.g., OperationalError, ProgrammingError), not CalledProcessError. The current handler will never catch actual database errors, causing the warning to never appear and potentially masking cleanup failures.

🐛 Proposed fix
         finally:
             try:
-                self._execute_sql("postgres", [f"DROP DATABASE IF EXISTS {temp_db};"])
-            except CalledProcessError:
+                drop_db_sql = sql.SQL("DROP DATABASE IF EXISTS {};").format(
+                    sql.Identifier(temp_db)
+                )
+                self._execute_sql("postgres", [drop_db_sql])
+            except Exception:
                 self.stderr.write(
                     self.style.WARNING(f"Failed to drop temp DB {temp_db} (ignored).")
                 )
🤖 Prompt for AI Agents
In `@backend/apps/common/management/commands/dump_data.py` around lines 87 - 93,
The finally block is catching CalledProcessError but _execute_sql raises
psycopg2 exceptions; change the exception handler to catch psycopg2.Error (or
psycopg2.DatabaseError) instead of CalledProcessError, and log the exception
details (e.g., include the exception message variable) in the warning so
failures in _execute_sql within dump_data.py are actually reported; reference
the _execute_sql call and replace the Except CalledProcessError branch with
Except psycopg2.Error as e and include e in the self.stderr.write warning.

Comment on lines +38 to +41
@strawberry_django.field
def url(self, root: PullRequest) -> str:
"""Resolve URL."""
return str(self.url) if self.url else ""
return root.url or ""
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the PullRequest model for the url field definition
rg -n "url\s*=" --type py backend/apps/github/models/pull_request.py | head -20

Repository: OWASP/Nest

Length of output: 36


🏁 Script executed:

#!/bin/bash
# Also check for the complete PullRequest model structure
find backend -name "pull_request.py" -path "*/models/*" -type f

Repository: OWASP/Nest

Length of output: 148


🏁 Script executed:

#!/bin/bash
# Search for url field definition in the github models
rg -n "url.*=" --type py backend/apps/github/models/ -A2 | grep -A2 "class PullRequest"

Repository: OWASP/Nest

Length of output: 36


🏁 Script executed:

cat backend/apps/github/models/pull_request.py

Repository: OWASP/Nest

Length of output: 4692


🏁 Script executed:

# Also check the other node files for url resolver patterns
rg -n "def url" --type py backend/apps/github/api/internal/nodes/ -A3

Repository: OWASP/Nest

Length of output: 1381


🏁 Script executed:

# Find the GenericIssueModel
find backend -name "*.py" -path "*/models/*" | xargs grep -l "class GenericIssueModel"

Repository: OWASP/Nest

Length of output: 103


🏁 Script executed:

# Search for GenericIssueModel definition
rg -n "class GenericIssueModel" --type py -A50

Repository: OWASP/Nest

Length of output: 4294


Remove the unnecessary empty string fallback.

The url field in GenericIssueModel is defined as models.URLField(verbose_name="URL", max_length=500, default="") — non-nullable with a default empty string. The fallback or "" in the resolver is redundant and should be removed to align with other node resolvers (RepositoryNode, UserNode, ReleaseNode, OrganizationNode), which all return root.url directly.

🤖 Prompt for AI Agents
In `@backend/apps/github/api/internal/nodes/pull_request.py` around lines 38 - 41,
In the url resolver method (def url(self, root: PullRequest) -> str) remove the
redundant fallback and return root.url directly (i.e., replace "return root.url
or \"\"" with "return root.url") so the PullRequest url field matches other node
resolvers like RepositoryNode/UserNode/ReleaseNode/OrganizationNode that return
root.url without an empty-string fallback.

Comment on lines 72 to +81
if distinct:
latest_issue_per_author = (
queryset.filter(author_id=OuterRef("author_id"))
queryset = (
queryset.annotate(
rank=Window(
expression=Rank(),
partition_by=[F("author_id")],
order_by=F("created_at").desc(),
)
)
.filter(rank=1)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd backend/apps/github/api/internal/queries && wc -l issue.py

Repository: OWASP/Nest

Length of output: 65


🏁 Script executed:

cd backend/apps/github/api/internal/queries && head -100 issue.py | cat -n

Repository: OWASP/Nest

Length of output: 3349


🏁 Script executed:

cd backend/apps/github/api/internal/queries && sed -n '50,90p' issue.py | cat -n

Repository: OWASP/Nest

Length of output: 1505


🏁 Script executed:

fd -e py -path "*/github/models/*" | head -10

Repository: OWASP/Nest

Length of output: 222


🏁 Script executed:

fd issue.py -path "*/github/models/*"

Repository: OWASP/Nest

Length of output: 222


🏁 Script executed:

cat backend/apps/github/models/issue.py | head -60

Repository: OWASP/Nest

Length of output: 1828


🏁 Script executed:

cat backend/apps/github/models/issue.py | grep -A 5 "repository"

Repository: OWASP/Nest

Length of output: 1554


🏁 Script executed:

cat backend/apps/github/models/generic_issue_model.py | head -80

Repository: OWASP/Nest

Length of output: 2224


Distinct logic ignores repository despite the docstring.

The docstring at line 51 promises uniqueness "per author and repository", but the window function partitions only by author_id (line 77). This means if the same author has issues across multiple repositories, only one issue per author is returned globally—not one per author-repository combination as documented. Add repository_id to the partition to align the implementation with its contract.

Proposed fix
-                        partition_by=[F("author_id")],
+                        partition_by=[F("author_id"), F("repository_id")],
🤖 Prompt for AI Agents
In `@backend/apps/github/api/internal/queries/issue.py` around lines 72 - 81, The
distinct logic in the queryset.annotate(rank=Window(...)) currently partitions
only by F("author_id") so it returns one issue per author globally; update the
partition_by argument in the Window expression used in this block (the Rank()
window annotation on queryset in the distinct branch) to include
F("repository_id") as well (i.e., partition_by=[F("author_id"),
F("repository_id")]) so uniqueness is enforced per author-and-repository as the
docstring promises.

Comment on lines +149 to 152
@cached_property
def project_requirements(self) -> ProjectHealthRequirements | None:
"""Get the project health requirements for the project's level."""
return ProjectHealthRequirements.objects.get(level=self.project.level)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n backend/apps/owasp/models/project_health_metrics.py | head -160

Repository: OWASP/Nest

Length of output: 7636


🏁 Script executed:

rg "project_requirements" --type py -A 2 -B 2

Repository: OWASP/Nest

Length of output: 3041


🏁 Script executed:

find . -name "project_health_requirements.py" -type f | head -5

Repository: OWASP/Nest

Length of output: 112


🏁 Script executed:

cat -n ./backend/apps/owasp/models/project_health_requirements.py

Repository: OWASP/Nest

Length of output: 3204


🏁 Script executed:

rg "class ProjectLevel" --type py -A 15

Repository: OWASP/Nest

Length of output: 1186


Use safe lookup to return None instead of raising for missing ProjectHealthRequirements.

The return type annotation ProjectHealthRequirements | None signals that None is a valid return value. All five accessor properties (lines 95, 105, 119, 129, 144-146) already handle this by checking if self.project_requirements before accessing attributes. However, ProjectHealthRequirements.objects.get(...) raises ObjectDoesNotExist if no record matches the project's level, breaking the contract and causing a 500 error instead of gracefully falling back to 0.

Suggested fix
 `@cached_property`
 def project_requirements(self) -> ProjectHealthRequirements | None:
     """Get the project health requirements for the project's level."""
-    return ProjectHealthRequirements.objects.get(level=self.project.level)
+    return ProjectHealthRequirements.objects.filter(level=self.project.level).first()
🤖 Prompt for AI Agents
In `@backend/apps/owasp/models/project_health_metrics.py` around lines 149 - 152,
The property project_requirements currently calls
ProjectHealthRequirements.objects.get(level=self.project.level) which will raise
if no record exists; change it to a safe lookup that returns None when missing
(e.g., use
ProjectHealthRequirements.objects.filter(level=self.project.level).first() or
catch ProjectHealthRequirements.DoesNotExist and return None) so the annotated
return type ProjectHealthRequirements | None is honored and the other accessors
can continue to check if self.project_requirements before use.

Comment on lines +10 to +49
echo "Fetching CSRF token..."
CSRF_TOKEN=$(curl -fsSL "$BASE_URL/csrf" | jq -r '.csrftoken')

if [ -z "$CSRF_TOKEN" ] || [ "$CSRF_TOKEN" = "null" ]; then
echo "Error: Failed to retrieve CSRF token." >&2
exit 1
fi

export CSRF_TOKEN

echo "CSRF token retrieved successfully."
:> ./schemathesis.toml

# Number of examples to generate per endpoint
# See https://schemathesis.readthedocs.io/en/stable/explanations/data-generation/#how-many-test-cases-does-schemathesis-generate

echo "generation.max-examples = 100" >> ./schemathesis.toml

# Enable specific checks
# See https://schemathesis.readthedocs.io/en/stable/reference/checks/
# Schemathesis raises errors for bad requests, so we need to explicitly enable the checks we want
echo "[checks]" >> ./schemathesis.toml
echo "enabled = false" >> ./schemathesis.toml
echo "response_schema_conformance.enabled = true" >> ./schemathesis.toml
echo "not_a_server_error.enabled = true" >> ./schemathesis.toml

if [ -n "$TEST_FILE" ]; then
echo "Using test file: $TEST_FILE"
else
echo "Error: TEST_FILE environment variable is not set." >&2
exit 1
fi

echo "Starting fuzzing process..."

if [ -n "$CI" ]; then
pytest ./tests/${TEST_FILE}
else
pytest --log-cli-level=INFO -s ./tests/${TEST_FILE}
fi
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, find the Dockerfile.fuzz file
fd -a 'Dockerfile.fuzz' -type f

Repository: OWASP/Nest

Length of output: 224


🏁 Script executed:

# Also locate the entrypoint.fuzz.sh file to confirm content
fd -a 'entrypoint.fuzz.sh' -type f

Repository: OWASP/Nest

Length of output: 224


🏁 Script executed:

# Search for any Dockerfile variations that might install these tools
fd -a 'Dockerfile' -type f | head -20

Repository: OWASP/Nest

Length of output: 224


🏁 Script executed:

# Find Dockerfile.fuzz file
fd 'Dockerfile.fuzz'

Repository: OWASP/Nest

Length of output: 84


🏁 Script executed:

# Find entrypoint.fuzz.sh file
fd 'entrypoint.fuzz.sh'

Repository: OWASP/Nest

Length of output: 80


🏁 Script executed:

# Find all Dockerfile files
fd 'Dockerfile'

Repository: OWASP/Nest

Length of output: 447


🏁 Script executed:

# Read the Dockerfile.fuzz to check for curl and jq installations
cat docker/backend/Dockerfile.fuzz

Repository: OWASP/Nest

Length of output: 953


🏁 Script executed:

# Read the entrypoint.fuzz.sh to verify the code matches the review
cat backend/entrypoint.fuzz.sh

Repository: OWASP/Nest

Length of output: 1438


Add curl timeouts and quote TEST_FILE variable.

The curl request to fetch the CSRF token lacks timeouts, which can cause the entrypoint to hang indefinitely in CI if the network is slow or unresponsive. Additionally, the unquoted TEST_FILE variable in the pytest commands can cause unintended shell globbing if the variable contains special characters or spaces.

🛠️ Suggested fixes
-CSRF_TOKEN=$(curl -fsSL "$BASE_URL/csrf" | jq -r '.csrftoken')
+CSRF_TOKEN=$(curl -fsSL --connect-timeout 5 --max-time 15 "$BASE_URL/csrf" | jq -r '.csrftoken')
@@
-    pytest ./tests/${TEST_FILE}
+    pytest "./tests/${TEST_FILE}"
@@
-    pytest --log-cli-level=INFO -s ./tests/${TEST_FILE}
+    pytest --log-cli-level=INFO -s "./tests/${TEST_FILE}"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
echo "Fetching CSRF token..."
CSRF_TOKEN=$(curl -fsSL "$BASE_URL/csrf" | jq -r '.csrftoken')
if [ -z "$CSRF_TOKEN" ] || [ "$CSRF_TOKEN" = "null" ]; then
echo "Error: Failed to retrieve CSRF token." >&2
exit 1
fi
export CSRF_TOKEN
echo "CSRF token retrieved successfully."
:> ./schemathesis.toml
# Number of examples to generate per endpoint
# See https://schemathesis.readthedocs.io/en/stable/explanations/data-generation/#how-many-test-cases-does-schemathesis-generate
echo "generation.max-examples = 100" >> ./schemathesis.toml
# Enable specific checks
# See https://schemathesis.readthedocs.io/en/stable/reference/checks/
# Schemathesis raises errors for bad requests, so we need to explicitly enable the checks we want
echo "[checks]" >> ./schemathesis.toml
echo "enabled = false" >> ./schemathesis.toml
echo "response_schema_conformance.enabled = true" >> ./schemathesis.toml
echo "not_a_server_error.enabled = true" >> ./schemathesis.toml
if [ -n "$TEST_FILE" ]; then
echo "Using test file: $TEST_FILE"
else
echo "Error: TEST_FILE environment variable is not set." >&2
exit 1
fi
echo "Starting fuzzing process..."
if [ -n "$CI" ]; then
pytest ./tests/${TEST_FILE}
else
pytest --log-cli-level=INFO -s ./tests/${TEST_FILE}
fi
echo "Fetching CSRF token..."
CSRF_TOKEN=$(curl -fsSL --connect-timeout 5 --max-time 15 "$BASE_URL/csrf" | jq -r '.csrftoken')
if [ -z "$CSRF_TOKEN" ] || [ "$CSRF_TOKEN" = "null" ]; then
echo "Error: Failed to retrieve CSRF token." >&2
exit 1
fi
export CSRF_TOKEN
echo "CSRF token retrieved successfully."
:> ./schemathesis.toml
# Number of examples to generate per endpoint
# See https://schemathesis.readthedocs.io/en/stable/explanations/data-generation/#how-many-test-cases-does-schemathesis-generate
echo "generation.max-examples = 100" >> ./schemathesis.toml
# Enable specific checks
# See https://schemathesis.readthedocs.io/en/stable/reference/checks/
# Schemathesis raises errors for bad requests, so we need to explicitly enable the checks we want
echo "[checks]" >> ./schemathesis.toml
echo "enabled = false" >> ./schemathesis.toml
echo "response_schema_conformance.enabled = true" >> ./schemathesis.toml
echo "not_a_server_error.enabled = true" >> ./schemathesis.toml
if [ -n "$TEST_FILE" ]; then
echo "Using test file: $TEST_FILE"
else
echo "Error: TEST_FILE environment variable is not set." >&2
exit 1
fi
echo "Starting fuzzing process..."
if [ -n "$CI" ]; then
pytest "./tests/${TEST_FILE}"
else
pytest --log-cli-level=INFO -s "./tests/${TEST_FILE}"
fi
🤖 Prompt for AI Agents
In `@backend/entrypoint.fuzz.sh` around lines 10 - 49, The curl call that sets
CSRF_TOKEN should include connect and overall timeouts to avoid hanging in CI
and fail fast on network issues (e.g., add appropriate --connect-timeout and
--max-time options to the curl invocation that produces CSRF_TOKEN); also
protect the pytest invocations from word-splitting/globbing by quoting the
TEST_FILE expansion in both branches (wrap the ./tests/${TEST_FILE} operand in
quotes) so filenames with spaces or special chars are handled safely.

Comment on lines +105 to +108
lint.per-file-ignores."**/management/commands/dump_data.py" = [
"S603", # https://docs.astral.sh/ruff/rules/subprocess-without-shell-equals-true/,
"S607", # https://docs.astral.sh/ruff/rules/start-process-with-partial-path/,
]
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -path "*/management/commands/dump_data.py" -type f

Repository: OWASP/Nest

Length of output: 108


🏁 Script executed:

cat -n backend/management/commands/dump_data.py

Repository: OWASP/Nest

Length of output: 126


🏁 Script executed:

cat -n ./backend/apps/common/management/commands/dump_data.py

Repository: OWASP/Nest

Length of output: 5241


🏁 Script executed:

sed -n '100,115p' backend/pyproject.toml

Repository: OWASP/Nest

Length of output: 974


Reconsider the S603 suppression and document the S607 justification.

The S603 rule (subprocess-without-shell-equals-true) is unnecessary—the code doesn't use shell=True on line 82, so this rule wouldn't trigger. The S607 suppression (start-process-with-partial-path) is justified since pg_dump is a system utility resolved via PATH. However, narrow the suppression to only S607, or add a comment explaining why S603 is included for future maintainers.

🤖 Prompt for AI Agents
In `@backend/pyproject.toml` around lines 105 - 108, Update the per-file ruff
ignores for lint.per-file-ignores."**/management/commands/dump_data.py": remove
the unnecessary "S603" entry (since dump_data.py does not use shell=True) and
keep only "S607", or if you prefer to keep S603 for future caution, add an
inline comment next to the ignore explaining why it's retained; reference the
ignore list key "lint.per-file-ignores.\"**/management/commands/dump_data.py\""
and the rule IDs "S603" and "S607" when making the change so reviewers can see
you either narrowed the suppression to S607 or documented the justification for
S603.

Comment on lines +22 to +28
healthcheck:
interval: 10s
retries: 10
test: >
sh -c '
wget --spider http://backend:9000/a/
'
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the docker-compose file to understand the backend setup
cat -n docker-compose/e2e/compose.yaml | head -30

Repository: OWASP/Nest

Length of output: 941


🏁 Script executed:

# Check if there are Dockerfiles for the backend
fd -type f -name 'Dockerfile*' | grep -i backend

Repository: OWASP/Nest

Length of output: 224


🏁 Script executed:

# Also search for wget installations in all Dockerfiles
rg -n 'wget|apk.*install|apt.*install' --glob 'Dockerfile*' | head -20

Repository: OWASP/Nest

Length of output: 36


🏁 Script executed:

# The compose file is at docker-compose/e2e/compose.yaml
# Dockerfile path is ../docker/backend/Dockerfile relative to compose.yaml
# So the actual path should be docker-compose/docker/backend/Dockerfile
fd -type f -name 'Dockerfile' docker-compose/

Repository: OWASP/Nest

Length of output: 224


🏁 Script executed:

# Also check if there are any Python-based health checks or other healthcheck implementations
rg -n 'healthcheck|health_check' --glob 'Dockerfile*' -A 3

Repository: OWASP/Nest

Length of output: 36


🏁 Script executed:

# Search for the backend Dockerfile more broadly
find . -name '*Dockerfile*' -path '*backend*' -o -name '*Dockerfile*' -path '*docker*' | head -20

Repository: OWASP/Nest

Length of output: 453


🏁 Script executed:

# Examine the backend Dockerfile
cat -n ./docker/backend/Dockerfile

Repository: OWASP/Nest

Length of output: 2223


🏁 Script executed:

# Also check if wget might be in the base Python image
# First, let's see what base image is used
head -20 ./docker/backend/Dockerfile

Repository: OWASP/Nest

Length of output: 568


🏁 Script executed:

# Let's verify if wget is available in python:3.13.7-alpine by checking if there are any package installations after the base image
tail -20 ./docker/backend/Dockerfile

Repository: OWASP/Nest

Length of output: 509


🏁 Script executed:

# Check if there's an entrypoint.sh that might install wget
cat -n ./backend/entrypoint.sh 2>/dev/null | head -20

Repository: OWASP/Nest

Length of output: 277


Install wget in the backend Dockerfile or switch to a Python-based healthcheck.

The backend image uses python:3.13.7-alpine, which does not include wget by default. The healthcheck will fail with "command not found", preventing the backend service from becoming healthy and blocking the entire e2e test suite.

Either add RUN apk add --no-cache wget to the Dockerfile, or replace the wget-based check with a Python-based health verification using curl or a direct Python HTTP request.

🤖 Prompt for AI Agents
In `@docker-compose/e2e/compose.yaml` around lines 22 - 28, The healthcheck for
the backend service uses wget but the backend image (python:3.13.7-alpine)
doesn't include it, causing the check to always fail; either install wget in the
backend Dockerfile by adding a package install (e.g., apk add --no-cache wget)
so the healthcheck's test: wget --spider http://backend:9000/a/ succeeds, or
replace the healthcheck block (healthcheck/test) with a Python-based check that
performs an HTTP GET to http://backend:9000/a/ (using python -c 'import
sys,urllib.request; urllib.request.urlopen("http://backend:9000/a/")' and
exiting nonzero on failure) so the backend service can become healthy.

Comment on lines +1 to +16
NEXT_PUBLIC_API_URL=http://localhost:9000/
NEXT_PUBLIC_CSRF_URL=http://localhost:9000/csrf/
NEXT_PUBLIC_ENVIRONMENT=local
NEXT_PUBLIC_GRAPHQL_URL=http://localhost:9000/graphql/
NEXT_PUBLIC_GTM_ID=
NEXT_PUBLIC_IDX_URL=http://localhost:9000/idx/
NEXT_PUBLIC_IS_PROJECT_HEALTH_ENABLED=true
NEXT_PUBLIC_RELEASE_VERSION=
NEXT_PUBLIC_SENTRY_DSN=
NEXT_SERVER_CSRF_URL=http://localhost:9000/csrf/
NEXT_SERVER_DISABLE_SSR=false
NEXT_SERVER_GITHUB_CLIENT_ID=your-github-client-id
NEXT_SERVER_GITHUB_CLIENT_SECRET=your-github-client-secret
NEXT_SERVER_GRAPHQL_URL=http://localhost:9000/graphql/
NEXTAUTH_SECRET=<your-nextauth-secret>
NEXTAUTH_URL=http://localhost:3000/
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix dotenv-linter UnorderedKey warnings.

dotenv-linter reports NEXTAUTH_* should appear before NEXT_PUBLIC_*. If lint is enforced, this example file will fail. Reorder the keys or exempt this file.

✅ Proposed ordering fix
+NEXTAUTH_SECRET=<your-nextauth-secret>
+NEXTAUTH_URL=http://localhost:3000/
 NEXT_PUBLIC_API_URL=http://localhost:9000/
 NEXT_PUBLIC_CSRF_URL=http://localhost:9000/csrf/
 NEXT_PUBLIC_ENVIRONMENT=local
 NEXT_PUBLIC_GRAPHQL_URL=http://localhost:9000/graphql/
 NEXT_PUBLIC_GTM_ID=
 NEXT_PUBLIC_IDX_URL=http://localhost:9000/idx/
 NEXT_PUBLIC_IS_PROJECT_HEALTH_ENABLED=true
 NEXT_PUBLIC_RELEASE_VERSION=
 NEXT_PUBLIC_SENTRY_DSN=
 NEXT_SERVER_CSRF_URL=http://localhost:9000/csrf/
 NEXT_SERVER_DISABLE_SSR=false
 NEXT_SERVER_GITHUB_CLIENT_ID=your-github-client-id
 NEXT_SERVER_GITHUB_CLIENT_SECRET=your-github-client-secret
 NEXT_SERVER_GRAPHQL_URL=http://localhost:9000/graphql/
-NEXTAUTH_SECRET=<your-nextauth-secret>
-NEXTAUTH_URL=http://localhost:3000/
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
NEXT_PUBLIC_API_URL=http://localhost:9000/
NEXT_PUBLIC_CSRF_URL=http://localhost:9000/csrf/
NEXT_PUBLIC_ENVIRONMENT=local
NEXT_PUBLIC_GRAPHQL_URL=http://localhost:9000/graphql/
NEXT_PUBLIC_GTM_ID=
NEXT_PUBLIC_IDX_URL=http://localhost:9000/idx/
NEXT_PUBLIC_IS_PROJECT_HEALTH_ENABLED=true
NEXT_PUBLIC_RELEASE_VERSION=
NEXT_PUBLIC_SENTRY_DSN=
NEXT_SERVER_CSRF_URL=http://localhost:9000/csrf/
NEXT_SERVER_DISABLE_SSR=false
NEXT_SERVER_GITHUB_CLIENT_ID=your-github-client-id
NEXT_SERVER_GITHUB_CLIENT_SECRET=your-github-client-secret
NEXT_SERVER_GRAPHQL_URL=http://localhost:9000/graphql/
NEXTAUTH_SECRET=<your-nextauth-secret>
NEXTAUTH_URL=http://localhost:3000/
NEXTAUTH_SECRET=<your-nextauth-secret>
NEXTAUTH_URL=http://localhost:3000/
NEXT_PUBLIC_API_URL=http://localhost:9000/
NEXT_PUBLIC_CSRF_URL=http://localhost:9000/csrf/
NEXT_PUBLIC_ENVIRONMENT=local
NEXT_PUBLIC_GRAPHQL_URL=http://localhost:9000/graphql/
NEXT_PUBLIC_GTM_ID=
NEXT_PUBLIC_IDX_URL=http://localhost:9000/idx/
NEXT_PUBLIC_IS_PROJECT_HEALTH_ENABLED=true
NEXT_PUBLIC_RELEASE_VERSION=
NEXT_PUBLIC_SENTRY_DSN=
NEXT_SERVER_CSRF_URL=http://localhost:9000/csrf/
NEXT_SERVER_DISABLE_SSR=false
NEXT_SERVER_GITHUB_CLIENT_ID=your-github-client-id
NEXT_SERVER_GITHUB_CLIENT_SECRET=your-github-client-secret
NEXT_SERVER_GRAPHQL_URL=http://localhost:9000/graphql/
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 15-15: [UnorderedKey] The NEXTAUTH_SECRET key should go before the NEXT_PUBLIC_API_URL key

(UnorderedKey)


[warning] 16-16: [UnorderedKey] The NEXTAUTH_URL key should go before the NEXT_PUBLIC_API_URL key

(UnorderedKey)

🤖 Prompt for AI Agents
In `@frontend/.env.e2e.example` around lines 1 - 16, Reorder the env keys so
NEXTAUTH_* entries appear before NEXT_PUBLIC_* to satisfy dotenv-linter
ordering: move NEXTAUTH_SECRET and NEXTAUTH_URL to above the NEXT_PUBLIC_API_URL
(or above the first NEXT_PUBLIC_* block), keeping all other keys unchanged;
ensure the file still contains NEXT_SERVER_* entries and secrets unchanged, or
alternatively add a dotenv-linter exemption comment if you prefer to keep the
current order.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend backend-tests ci docker Pull requests that update Docker code docs Improvements or additions to documentation frontend makefile

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants