Skip to content

Added sponsor support#4511

Open
anurag2787 wants to merge 6 commits intoOWASP:mainfrom
anurag2787:sponser-page
Open

Added sponsor support#4511
anurag2787 wants to merge 6 commits intoOWASP:mainfrom
anurag2787:sponser-page

Conversation

@anurag2787
Copy link
Copy Markdown
Contributor

Proposed change

Adds the initial sponsor support for OWASP Nest.

This includes extending the sponsor model levels, status, optional chapter/project links, a dedicated /sponsors page to list active sponsors, an application flow that creates draft records, and a REST API to fetch active sponsors.

Resolves #4259

Screencast.from.2026-04-07.00-12-41.webm

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
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2026

Warning

Rate limit exceeded

@anurag2787 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 17 minutes and 26 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 17 minutes and 26 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 14d8de18-7221-4ac3-a199-4f8f6773193e

📥 Commits

Reviewing files that changed from the base of the PR and between a98322f and b2e5325.

📒 Files selected for processing (1)
  • frontend/src/app/projects/[projectKey]/page.tsx

Walkthrough

Adds sponsor model fields and migration, REST and GraphQL sponsor application endpoints, GraphQL sponsor fields/resolvers, admin UI updates, frontend sponsor pages/components/queries/mutations, and a Docker volume rename for local DB data.

Changes

Cohort / File(s) Summary
Backend Model & Migration
backend/apps/owasp/models/sponsor.py, backend/apps/owasp/migrations/0073_sponsor_chapter_sponsor_contact_email_and_more.py
Adds Sponsor.Status (draft/active/archived), status, contact_email, nullable chapter/project FKs (related_name='sponsors'), readable_status property; migration creates corresponding DB columns.
REST API & Project Schema
backend/apps/api/rest/v0/sponsor.py, backend/apps/api/rest/v0/project.py
Adds POST /applications/ REST handler and request/response schemas with validation, slug key derivation, duplicate check, draft creation and error handling; extends ProjectDetail with ProjectSponsor schema and resolve_sponsors filtering active sponsors.
GraphQL Mutations & Registration
backend/apps/owasp/api/internal/mutations/sponsor.py, backend/apps/owasp/api/internal/mutations/__init__.py, backend/settings/graphql.py
Adds SponsorMutations.create_sponsor_application and CreateSponsorApplicationResult; registers OwaspMutations into global Mutation type.
GraphQL Nodes & Queries
backend/apps/owasp/api/internal/nodes/sponsor.py, .../nodes/project.py, .../nodes/chapter.py, .../queries/sponsor.py
Exposes additional Sponsor fields (description, id, key, status); adds sponsors field on ProjectNode and ChapterNode with prefetch and active-status filter; sponsor list query now filters to active sponsors.
Django Admin
backend/apps/owasp/admin/sponsor.py
Updates SponsorAdmin: add status, chapter, project to list_display/list_filter; add contact_email to search_fields and fieldsets; adds "Entity Associations" fieldset and sets nest_created_at/nest_updated_at readonly.
Frontend - Pages & Components
frontend/src/app/sponsors/page.tsx, frontend/src/app/sponsors/apply/page.tsx, frontend/src/app/chapters/[chapterKey]/page.tsx, frontend/src/app/projects/[projectKey]/page.tsx
Adds sponsors listing page (tiered, active-only) and sponsor application page with client validation and mutation; chapter/project pages map/forward sponsor data to details component.
Frontend - Sponsor UI & Types
frontend/src/components/SponsorBadge.tsx, frontend/src/components/CardDetailsPage.tsx, frontend/src/types/sponsor.ts, frontend/src/types/card.ts, frontend/src/types/home.ts
Adds SponsorBadge (compact/card), SponsorList, SponsorInfo/Sponsor types; DetailsCard accepts optional sponsors and conditionally renders sponsor section; updates related TS types.
Frontend - GraphQL Documents
frontend/src/server/mutations/sponsorMutations.ts, frontend/src/server/queries/sponsorQueries.ts, frontend/src/server/queries/chapterQueries.ts, frontend/src/server/queries/projectQueries.ts
Adds CREATE_SPONSOR_APPLICATION mutation and GET_SPONSORS query; extends chapter/project queries to request sponsor fields.
Infrastructure
docker-compose/local/compose.yaml
Renames DB compose volume from db-data to db-data-issue-4259 and updates service mount reference.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • kasya
  • arkid15r
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes one out-of-scope change: docker-compose volume renaming from 'db-data' to 'db-data-issue-4259', which is unrelated to sponsor support implementation and appears to be a local development artifact. Revert the docker-compose volume name change to 'db-data' as it is unrelated to sponsor feature implementation and should not be included in this PR.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Added sponsor support' is a concise, clear summary of the main change in the changeset—adding comprehensive sponsor functionality to the OWASP Nest platform.
Description check ✅ Passed The description is directly related to the changeset, detailing the sponsor model extensions, public /sponsors page, application flow, and REST API—all reflected in the code changes. It includes issue resolution reference, though some checklist items remain incomplete.
Linked Issues check ✅ Passed The PR fully implements core requirements from #4259: extended sponsor model with status/level fields [backend models, migration], public /sponsors page with active sponsor filtering [frontend page], /sponsors/apply form creating draft records [frontend form + backend mutation], and REST API endpoint for active sponsors [backend REST endpoint]. All primary objectives are satisfied.
Docstring Coverage ✅ Passed Docstring coverage is 93.75% 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 unit tests (beta)
  • Create PR with unit tests

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.

Copy link
Copy Markdown
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.

3 issues found across 31 files

Confidence score: 3/5

  • There is some merge risk because backend/apps/api/rest/v0/sponsor.py persists sponsor email/website without model validation, which can let malformed contact data into storage and create downstream data-quality issues.
  • backend/apps/api/rest/v0/sponsor.py also returns raw exception text in API responses, which may expose internal validation or database details to clients (moderate security/robustness concern).
  • backend/apps/owasp/api/internal/mutations/sponsor.py has a non-atomic exists() + save() flow on a unique key; under concurrency this can surface IntegrityError and currently map duplicates to a generic ERROR, so duplicate handling can be inconsistent.
  • Pay close attention to backend/apps/api/rest/v0/sponsor.py and backend/apps/owasp/api/internal/mutations/sponsor.py - validation, error exposure, and duplicate-submission handling need tightening.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="backend/apps/owasp/api/internal/mutations/sponsor.py">

<violation number="1" location="backend/apps/owasp/api/internal/mutations/sponsor.py:73">
P2: Non-atomic exists() check + save() on a unique key can raise IntegrityError under concurrent duplicate submissions, which is caught and returned as a generic ERROR instead of DUPLICATE, making duplicate handling nondeterministic.</violation>
</file>

<file name="backend/apps/api/rest/v0/sponsor.py">

<violation number="1" location="backend/apps/api/rest/v0/sponsor.py:163">
P2: Sponsor application persists email/website without running model validation, allowing malformed contact data to be stored.</violation>

<violation number="2" location="backend/apps/api/rest/v0/sponsor.py:175">
P2: Raw exception messages are returned in the API response, potentially leaking internal validation/DB details to clients.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Copy Markdown
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: 8

🧹 Nitpick comments (3)
frontend/src/app/projects/[projectKey]/page.tsx (1)

37-54: Simplify by using the imported SponsorInfo type for the input shape.

The inline type annotation (lines 38-46) duplicates the sponsor shape. Since SponsorInfo is already imported, consider using a more concise approach or defining a reusable input type if the GraphQL response shape differs.

♻️ Proposed simplification

If the GraphQL response matches SponsorInfo closely, you can simplify:

- const sponsors: SponsorInfo[] = (project?.sponsors || []).map(
-   (sponsor: {
-     key: string
-     name: string
-     sponsorType: string
-     imageUrl: string
-     url: string
-     description?: string
-     status?: string
-   }) => ({
+ const sponsors: SponsorInfo[] = (project?.sponsors || []).map(
+   (sponsor) => ({
      key: sponsor.key,
      name: sponsor.name,
      sponsorType: sponsor.sponsorType,
      imageUrl: sponsor.imageUrl,
      url: sponsor.url,
      description: sponsor.description || '',
    })
  )

TypeScript can infer the type from the GraphQL generated types.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/app/projects/`[projectKey]/page.tsx around lines 37 - 54, The
inline sponsor parameter type duplicates the imported SponsorInfo; update the
mapping to leverage SponsorInfo instead of the inline annotation: treat
project?.sponsors as an array of SponsorInfo (or create a small alias/type like
ProjectSponsor if the GraphQL shape truly differs) and remove the long inline
type in the map callback so the mapping uses SponsorInfo fields directly when
building the sponsors array (refer to the sponsors constant, SponsorInfo type,
and project?.sponsors to locate the code).
frontend/src/types/sponsor.ts (1)

1-9: Consolidate duplicate Sponsor type definitions.

The Sponsor type defined here is identical to the one in types/home.ts, which is currently imported by LogoCarousel.tsx and sponsors/page.tsx. Consider:

  1. Removing this file and importing Sponsor from types/home.ts where needed, or
  2. Moving the canonical definition here and updating existing imports if types/sponsor.ts better reflects the module organization.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/types/sponsor.ts` around lines 1 - 9, Duplicate Sponsor type
definitions exist between the Sponsor type here and the one in types/home.ts;
choose one canonical source and remove the duplicate: either delete this
types/sponsor.ts and update all imports (e.g., in LogoCarousel.tsx and
sponsors/page.tsx) to import Sponsor from types/home.ts, or keep this file as
the canonical definition and change imports in LogoCarousel.tsx and
sponsors/page.tsx (and any other modules) to import Sponsor from
types/sponsor.ts; ensure a single exported type named Sponsor remains
project-wide and run TypeScript to verify no broken imports.
frontend/src/components/SponsorBadge.tsx (1)

183-190: Align SponsorListProps typing with runtime behavior.

SponsorListProps.sponsors is required, but the component handles undefined/null. Making it optional improves contract consistency.

♻️ Proposed fix
 interface SponsorListProps {
-  sponsors: SponsorInfo[]
+  sponsors?: SponsorInfo[]
   variant?: 'compact' | 'card'
   title?: string
 }
 
 export const SponsorList: React.FC<SponsorListProps> = ({ sponsors, variant = 'card', title }) => {
-  if (!sponsors || sponsors.length === 0) return null
+  if (!sponsors?.length) return null
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/SponsorBadge.tsx` around lines 183 - 190, The
component SponsorList handles null/undefined sponsors at runtime but the
SponsorListProps interface currently declares sponsors as required; update
SponsorListProps to make sponsors optional (sponsors?: SponsorInfo[]) so the
type matches runtime behavior, and then audit usages of SponsorList and adjust
any callers that relied on a required prop (or add safe guards) to ensure no new
type errors occur; refer to the SponsorList component and SponsorListProps type
to locate the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/apps/api/rest/v0/sponsor.py`:
- Around line 145-177: Validate and sanitize payload.name,
payload.contact_email, and payload.sponsorship_interest by trimming and
rejecting empty/whitespace-only values; compute key = slugify(trimmed_name) and
if key == "" return a BAD_REQUEST explaining an invalid organization name;
populate SponsorModel with trimmed values, call sponsor.full_clean() before
sponsor.save() to enforce model validators (EmailField/URLField) and catch
ValidationError/IntegrityError but do not return the raw exception text—instead
log the exception and return a generic "Error creating sponsor application"
message; ensure SponsorApplicationResponse is returned on success with
sponsor.id/name/status as before.

In `@backend/apps/owasp/api/internal/mutations/sponsor.py`:
- Around line 80-90: The Sponsor instance is being saved without running field
validators so malformed contact_email or url can be persisted; call
sponsor.full_clean() after constructing the Sponsor (before sponsor.save()) to
run field and model validators, and update the surrounding exception handling to
catch django.core.exceptions.ValidationError (instead of or in addition to other
exceptions) and handle/report validation errors appropriately where
Sponsor.full_clean() is invoked in this function.

In `@docker-compose/local/compose.yaml`:
- Line 70: Replace the temporary feature-branch volume name db-data-issue-4259
with the canonical db-data in the docker-compose local compose.yaml entry (and
any other occurrences such as the one noted at the later occurrence), so the
service uses the shared db-data volume on main; search for the symbol
db-data-issue-4259 and change it back to db-data before merging.

In `@frontend/src/app/sponsors/page.tsx`:
- Around line 63-68: The sponsor card currently always renders a Link with
href="#" for sponsors missing sponsor.url, producing fake interactive elements;
update the rendering in frontend/src/app/sponsors/page.tsx so that when
sponsor.url is present you render the existing <Link key={sponsor.id}
href={sponsor.url} ...> variant, but when sponsor.url is falsy render a plain
non-interactive container (e.g., a <div> or <article>) with the same visual
styles minus interactive affordances like cursor-pointer, target/rel, and hover
scale/shadow; apply the same conditional change for the second occurrence
referenced (the block around the lines noted for 117-122) so non-linked sponsors
are non-navigable and don’t show link behaviors.

In `@frontend/src/components/SponsorBadge.tsx`:
- Around line 87-90: The Link usages in SponsorBadge.tsx currently pass raw
sponsor.url (href={sponsor.url || '#'}), which can be unsafe or malformed; add a
small URL normalization/validation helper (e.g., normalizeSponsorUrl) used where
sponsor.url is consumed (both the Link at the top and the other occurrence
around lines 134-137) that: verifies the value is a non-empty string, uses the
URL constructor or a regex to ensure the protocol is http or https, prepends
"https://" if a domain-only value is provided, and otherwise returns '#' as a
safe fallback; replace direct uses of sponsor.url with this normalized value in
the Link href and keep rel/target unchanged.
- Around line 96-101: The SponsorBadge component uses sponsor.imageUrl (in
frontend/src/components/SponsorBadge.tsx) to render Image elements which will be
blocked by Next.js unless their hosts are allowed; update
frontend/next.config.ts remotePatterns to include the external domains serving
sponsor.imageUrl (or add a safe wildcard if sponsors are trusted) OR add URL
validation inside SponsorBadge (check host against an allow-list and render a
fallback image or placeholder when not allowed). Specifically, either add each
unique host/domain pattern to the next.config.ts remotePatterns array or
implement a small helper (used by SponsorBadge) that parses sponsor.imageUrl,
verifies host against a maintained allow-list, and returns a fallback URL when
the host is not permitted.

In `@frontend/src/types/card.ts`:
- Around line 87-94: The inline sponsor type in DetailsCardProps duplicates and
conflicts with the canonical Sponsor type; replace the inline sponsors
definition with the shared SponsorInfo (or the extracted reusable Sponsor type)
used by SponsorBadge.tsx/home.ts so types are consistent—import SponsorInfo from
SponsorBadge.tsx (or create/export a shared Sponsor type from home.ts) and
change the sponsors?: ...[] declaration in DetailsCardProps to use that
imported/shared type, preserving optional fields like url?: string.

In `@frontend/src/types/home.ts`:
- Line 43: LogoCarousel passes the optional Sponsor.url directly into Link which
can be undefined; update the LogoCarousel component to use a safe fallback
(e.g., pass sponsor.url || '#' to Link or conditionally render Link only when
sponsor.url exists) so links are never invalid; locate the LogoCarousel
component and replace direct uses of sponsor.url with the fallback pattern
consistent with SponsorBadge and sponsors/page (use sponsor.url || '#') or guard
rendering accordingly.

---

Nitpick comments:
In `@frontend/src/app/projects/`[projectKey]/page.tsx:
- Around line 37-54: The inline sponsor parameter type duplicates the imported
SponsorInfo; update the mapping to leverage SponsorInfo instead of the inline
annotation: treat project?.sponsors as an array of SponsorInfo (or create a
small alias/type like ProjectSponsor if the GraphQL shape truly differs) and
remove the long inline type in the map callback so the mapping uses SponsorInfo
fields directly when building the sponsors array (refer to the sponsors
constant, SponsorInfo type, and project?.sponsors to locate the code).

In `@frontend/src/components/SponsorBadge.tsx`:
- Around line 183-190: The component SponsorList handles null/undefined sponsors
at runtime but the SponsorListProps interface currently declares sponsors as
required; update SponsorListProps to make sponsors optional (sponsors?:
SponsorInfo[]) so the type matches runtime behavior, and then audit usages of
SponsorList and adjust any callers that relied on a required prop (or add safe
guards) to ensure no new type errors occur; refer to the SponsorList component
and SponsorListProps type to locate the change.

In `@frontend/src/types/sponsor.ts`:
- Around line 1-9: Duplicate Sponsor type definitions exist between the Sponsor
type here and the one in types/home.ts; choose one canonical source and remove
the duplicate: either delete this types/sponsor.ts and update all imports (e.g.,
in LogoCarousel.tsx and sponsors/page.tsx) to import Sponsor from types/home.ts,
or keep this file as the canonical definition and change imports in
LogoCarousel.tsx and sponsors/page.tsx (and any other modules) to import Sponsor
from types/sponsor.ts; ensure a single exported type named Sponsor remains
project-wide and run TypeScript to verify no broken imports.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b244e6b0-33ca-4af2-9198-7ff954138e59

📥 Commits

Reviewing files that changed from the base of the PR and between 2e41260 and 3f1e8e8.

⛔ Files ignored due to path filters (5)
  • frontend/src/types/__generated__/chapterQueries.generated.ts is excluded by !**/__generated__/**
  • frontend/src/types/__generated__/graphql.ts is excluded by !**/__generated__/**
  • frontend/src/types/__generated__/projectQueries.generated.ts is excluded by !**/__generated__/**
  • frontend/src/types/__generated__/sponsorMutations.generated.ts is excluded by !**/__generated__/**
  • frontend/src/types/__generated__/sponsorQueries.generated.ts is excluded by !**/__generated__/**
📒 Files selected for processing (26)
  • backend/apps/api/rest/v0/project.py
  • backend/apps/api/rest/v0/sponsor.py
  • backend/apps/owasp/admin/sponsor.py
  • backend/apps/owasp/api/internal/mutations/__init__.py
  • backend/apps/owasp/api/internal/mutations/sponsor.py
  • backend/apps/owasp/api/internal/nodes/chapter.py
  • backend/apps/owasp/api/internal/nodes/project.py
  • backend/apps/owasp/api/internal/nodes/sponsor.py
  • backend/apps/owasp/api/internal/queries/sponsor.py
  • backend/apps/owasp/migrations/0073_sponsor_chapter_sponsor_contact_email_and_more.py
  • backend/apps/owasp/models/sponsor.py
  • backend/settings/graphql.py
  • docker-compose/local/compose.yaml
  • frontend/src/app/chapters/[chapterKey]/page.tsx
  • frontend/src/app/projects/[projectKey]/page.tsx
  • frontend/src/app/sponsors/apply/page.tsx
  • frontend/src/app/sponsors/page.tsx
  • frontend/src/components/CardDetailsPage.tsx
  • frontend/src/components/SponsorBadge.tsx
  • frontend/src/server/mutations/sponsorMutations.ts
  • frontend/src/server/queries/chapterQueries.ts
  • frontend/src/server/queries/projectQueries.ts
  • frontend/src/server/queries/sponsorQueries.ts
  • frontend/src/types/card.ts
  • frontend/src/types/home.ts
  • frontend/src/types/sponsor.ts

Copy link
Copy Markdown
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: 3

♻️ Duplicate comments (1)
frontend/src/app/sponsors/page.tsx (1)

90-93: ⚠️ Potential issue | 🟡 Minor

Non-link cards still show interactive hover behavior.

For sponsors without URLs, the fallback <div> still uses hover scale/shadow classes, so non-clickable cards look interactive. Consider a non-hover class variant for non-link cards.

Also applies to: 163-166

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/app/sponsors/page.tsx` around lines 90 - 93, The current
implementation builds baseClassName with hover/scale/shadow utilities and sets
interactiveClassName = sponsor.url ? `cursor-pointer ${baseClassName}` :
baseClassName, causing non-link sponsor cards to still display hover behavior;
change this by defining a separate nonInteractiveClassName that removes
hover/scale/shadow classes (e.g., omit hover:scale[...] hover:shadow-lg
dark:hover:shadow-xl) and then use interactiveClassName = sponsor.url ?
`cursor-pointer ${baseClassName}` : nonInteractiveClassName (also update the
same pattern used around the other occurrence referenced by the comment for
lines 163-166) so only link cards show interactive hover styles.
🧹 Nitpick comments (1)
backend/apps/api/rest/v0/sponsor.py (1)

133-198: Consider throttling this public write endpoint.

POST /applications/ is unauthenticated and creates DB records. Add request throttling (and optionally CAPTCHA) to reduce spam/abuse risk.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/apps/api/rest/v0/sponsor.py` around lines 133 - 198, This public
unauthenticated write endpoint create_sponsor_application should be protected
from abuse by adding request throttling (and optionally CAPTCHA); modify the
router.post-backed handler create_sponsor_application to enforce a rate limit by
either applying an existing rate-limiting decorator/middleware (e.g., a
django-ratelimit or DRF throttle decorator) to the function or wiring a throttle
class into the routing layer that wraps routes registered by router.post, and
ensure checks run before creating SponsorModel records (so add the
throttle/CAPTCHA check at the top of create_sponsor_application, returning HTTP
429 on limit exceeded). Keep the existing validation and SponsorModel logic
intact but short-circuit when the throttle or CAPTCHA fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/apps/api/rest/v0/sponsor.py`:
- Around line 137-140: The response decorator currently maps
HTTPStatus.BAD_REQUEST to ValidationErrorSchema; change it to use SponsorError
instead so the 400 schema matches actual payloads; locate the response= dict in
the sponsor application endpoints (the decorator that currently references
ValidationErrorSchema) and replace HTTPStatus.BAD_REQUEST: ValidationErrorSchema
with HTTPStatus.BAD_REQUEST: SponsorError for the Sponsor application routes
(same pattern used earlier where SponsorError is already applied).

In `@frontend/src/app/sponsors/page.tsx`:
- Around line 16-33: Change the source list to only active sponsors before
grouping: replace the current "sponsors" array used by the grouping logic with a
filtered list that keeps only items where sponsor.status === "active" (the code
that defines const sponsors and the subsequent sponsors.forEach that uses
sponsor.sponsorType and pushes into groups). Ensure the filter is applied where
sponsors is defined (the value passed into the grouping useMemo) so groups
(diamond/platinum/gold/silver/supporter) only receives active sponsors.
- Around line 98-100: The link hrefs render untrusted sponsor.url values (seen
where href={sponsor.url} around the sponsor listing blocks) and must be
validated to allow only http: and https: schemes; add a small frontend sanitizer
util (e.g., isValidHttpUrl or sanitizeSponsorUrl) and use it in the
page/component that renders sponsors (the component where href={sponsor.url}
appears and the repeated block later) to either normalize/force https for
scheme-less values or skip/render a safe placeholder (e.g., '#') when the URL
fails validation, and ensure the sanitized value is what's passed into href and
target/rel attributes.

---

Duplicate comments:
In `@frontend/src/app/sponsors/page.tsx`:
- Around line 90-93: The current implementation builds baseClassName with
hover/scale/shadow utilities and sets interactiveClassName = sponsor.url ?
`cursor-pointer ${baseClassName}` : baseClassName, causing non-link sponsor
cards to still display hover behavior; change this by defining a separate
nonInteractiveClassName that removes hover/scale/shadow classes (e.g., omit
hover:scale[...] hover:shadow-lg dark:hover:shadow-xl) and then use
interactiveClassName = sponsor.url ? `cursor-pointer ${baseClassName}` :
nonInteractiveClassName (also update the same pattern used around the other
occurrence referenced by the comment for lines 163-166) so only link cards show
interactive hover styles.

---

Nitpick comments:
In `@backend/apps/api/rest/v0/sponsor.py`:
- Around line 133-198: This public unauthenticated write endpoint
create_sponsor_application should be protected from abuse by adding request
throttling (and optionally CAPTCHA); modify the router.post-backed handler
create_sponsor_application to enforce a rate limit by either applying an
existing rate-limiting decorator/middleware (e.g., a django-ratelimit or DRF
throttle decorator) to the function or wiring a throttle class into the routing
layer that wraps routes registered by router.post, and ensure checks run before
creating SponsorModel records (so add the throttle/CAPTCHA check at the top of
create_sponsor_application, returning HTTP 429 on limit exceeded). Keep the
existing validation and SponsorModel logic intact but short-circuit when the
throttle or CAPTCHA fails.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3a7d1c89-28f3-484f-b9ec-7a991c517423

📥 Commits

Reviewing files that changed from the base of the PR and between 3f1e8e8 and 8449e13.

📒 Files selected for processing (8)
  • backend/apps/api/rest/v0/sponsor.py
  • backend/apps/owasp/api/internal/mutations/sponsor.py
  • backend/apps/owasp/migrations/0073_sponsor_chapter_sponsor_contact_email_and_more.py
  • frontend/src/app/sponsors/apply/page.tsx
  • frontend/src/app/sponsors/page.tsx
  • frontend/src/components/SponsorBadge.tsx
  • frontend/src/types/card.ts
  • frontend/src/types/home.ts
✅ Files skipped from review due to trivial changes (2)
  • backend/apps/owasp/api/internal/mutations/sponsor.py
  • backend/apps/owasp/migrations/0073_sponsor_chapter_sponsor_contact_email_and_more.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • frontend/src/types/home.ts
  • frontend/src/types/card.ts
  • frontend/src/app/sponsors/apply/page.tsx
  • frontend/src/components/SponsorBadge.tsx

Copy link
Copy Markdown
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.

3 issues found across 8 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="backend/apps/owasp/api/internal/mutations/sponsor.py">

<violation number="1" location="backend/apps/owasp/api/internal/mutations/sponsor.py:74">
P1: Validation is executed after `get_or_create` persistence, so failed `full_clean()` can still leave a saved sponsor row and cause duplicate behavior on retry.</violation>
</file>

<file name="frontend/src/app/sponsors/apply/page.tsx">

<violation number="1" location="frontend/src/app/sponsors/apply/page.tsx:56">
P2: Email validation was weakened, causing malformed addresses to pass client-side validation.</violation>
</file>

<file name="frontend/src/app/sponsors/page.tsx">

<violation number="1" location="frontend/src/app/sponsors/page.tsx:98">
P1: Sanitize `sponsor.url` before passing it to `href`. This PR already adds `getSafeExternalHref` in `SponsorBadge.tsx` that validates URLs to only allow `http:`/`https:` schemes — use it here too. Without sanitization, a stored URL with a dangerous scheme (e.g. `javascript:`) would be rendered directly as a clickable link, creating an XSS vector.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Copy Markdown
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.

1 issue found across 4 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="backend/apps/owasp/api/internal/mutations/sponsor.py">

<violation number="1" location="backend/apps/owasp/api/internal/mutations/sponsor.py:88">
P2: Pre-validating with full_clean() will raise a ValidationError on existing sponsors (key is unique), causing duplicates to return the generic ERROR response instead of the intended DUPLICATE handling.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

coderabbitai[bot]
coderabbitai bot previously approved these changes Apr 6, 2026
coderabbitai[bot]
coderabbitai bot previously approved these changes Apr 6, 2026
@anurag2787 anurag2787 marked this pull request as ready for review April 6, 2026 20:20
Copy link
Copy Markdown
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.

🧹 Nitpick comments (1)
backend/apps/api/rest/v0/sponsor.py (1)

167-171: Consider distinguishing duplicate-key errors for better UX.

There's an inherent TOCTOU (time-of-check-to-time-of-use) gap between the exists() check and save(). While the IntegrityError catch provides a safety net (the key field has a unique=True constraint), a race condition could result in the generic "Error creating sponsor application" message instead of the more helpful "A sponsor with this organization name already exists" message.

This is a minor edge case since: (1) the data is protected by the DB constraint, and (2) concurrent applications with identical names are unlikely. No immediate action required, but you could optionally catch IntegrityError separately to provide a better user message.

♻️ Optional: Separate IntegrityError handling for better messaging
-    except (ValueError, ValidationError, IntegrityError) as e:
-        logger.warning("Error creating sponsor application: %s", e)
-        return Response(
-            {"message": "Error creating sponsor application"},
-            status=HTTPStatus.BAD_REQUEST,
-        )
+    except IntegrityError as e:
+        logger.warning("Duplicate sponsor key or integrity error: %s", e)
+        return Response(
+            {"message": "A sponsor with this organization name already exists"},
+            status=HTTPStatus.BAD_REQUEST,
+        )
+    except (ValueError, ValidationError) as e:
+        logger.warning("Error creating sponsor application: %s", e)
+        return Response(
+            {"message": "Error creating sponsor application"},
+            status=HTTPStatus.BAD_REQUEST,
+        )

Also applies to: 193-198

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/apps/api/rest/v0/sponsor.py` around lines 167 - 171, The Sponsor
creation flow currently checks SponsorModel.objects.filter(key=key).exists()
then later saves, which can race and yield a generic IntegrityError; update the
exception handling around the save in the sponsor creation view (the function
handling the POST that calls SponsorModel.save()/SponsorModel.objects.create())
to catch django.db.IntegrityError specifically and return the user-friendly
Response({"message": "A sponsor with this organization name already exists"},
status=HTTPStatus.BAD_REQUEST) when the IntegrityError indicates a unique key
violation, while keeping the broader Exception handler for other errors; apply
the same pattern to the similar block referenced around the other save path (the
second save block around lines 193-198).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@backend/apps/api/rest/v0/sponsor.py`:
- Around line 167-171: The Sponsor creation flow currently checks
SponsorModel.objects.filter(key=key).exists() then later saves, which can race
and yield a generic IntegrityError; update the exception handling around the
save in the sponsor creation view (the function handling the POST that calls
SponsorModel.save()/SponsorModel.objects.create()) to catch
django.db.IntegrityError specifically and return the user-friendly
Response({"message": "A sponsor with this organization name already exists"},
status=HTTPStatus.BAD_REQUEST) when the IntegrityError indicates a unique key
violation, while keeping the broader Exception handler for other errors; apply
the same pattern to the similar block referenced around the other save path (the
second save block around lines 193-198).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5f2fa8c9-8667-4c2a-a84c-dd8350cf1758

📥 Commits

Reviewing files that changed from the base of the PR and between 8449e13 and a98322f.

📒 Files selected for processing (4)
  • backend/apps/api/rest/v0/sponsor.py
  • backend/apps/owasp/api/internal/mutations/sponsor.py
  • frontend/src/app/sponsors/apply/page.tsx
  • frontend/src/app/sponsors/page.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/src/app/sponsors/page.tsx
  • frontend/src/app/sponsors/apply/page.tsx
  • backend/apps/owasp/api/internal/mutations/sponsor.py

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 6, 2026

Copy link
Copy Markdown
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.

0 issues found across 1 file (changes from recent commits).

@anurag2787
Copy link
Copy Markdown
Contributor Author

Hi @arkid15r Could you please review and let me know if any changes required? or should i start writing test for this

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.

Implement Sponsors Program Support

1 participant