Skip to content

feat(positions): protect positions/portfolio endpoint with CaptchaGuard#3106

Draft
PooyaRaki wants to merge 3 commits into
mainfrom
feat/positions-captcha-guard
Draft

feat(positions): protect positions/portfolio endpoint with CaptchaGuard#3106
PooyaRaki wants to merge 3 commits into
mainfrom
feat/positions-captcha-guard

Conversation

@PooyaRaki

@PooyaRaki PooyaRaki commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Apply the existing CaptchaGuard to the positions/portfolio endpoint so it requires a valid Cloudflare Turnstile token (when CAPTCHA is enabled in config), matching the protection already applied to GET /v{2,3}/owners/:ownerAddress/safes. When captcha.enabled is false (the default), the guard is a no-op and behavior is unchanged.

Changes

  • src/modules/positions/routes/positions.controller.ts — decorate getPositions (GET /v1/chains/:chainId/safes/:safeAddress/positions/:fiatCode) with @UseGuards(CaptchaGuard).
  • src/modules/positions/positions.module.ts — import CaptchaModule so the
    guard's dependencies (CaptchaService) are resolvable.
  • src/modules/positions/routes/positions.controller.integration.spec.ts — new integration spec exercising the guard end-to-end:
    • 401 when the x-captcha-token header is missing (no downstream call made)
    • 401 when Turnstile verification returns success: false
    • 200 when verification succeeds and the request reaches the Zerion data source (mocked to return an empty list)
    • src/modules/portfolio/portfolio.module.ts — import CaptchaModule.
    • src/modules/portfolio/v1/portfolio.controller.ts:85 — @UseGuards(CaptchaGuard) on GET /v1/portfolio/:address (DELETE is just a cache clear, left unguarded to match positions' GET-only scope).
    • src/modules/portfolio/v1/portfolio.controller.integration.spec.ts — new integration spec covering missing token (401), invalid token (401), and valid token (200, captcha verified) — 3/3 pass; biome and tsc clean; existing unit suite (10/10) still green.

The existing unit tests in positions.controller.spec.ts continue to pass — they instantiate the controller directly and bypass guards by design.

Test plan

  • yarn test:unit src/modules/positions/routes/positions.controller.spec.ts
  • yarn test:integration src/modules/positions/routes/positions.controller.integration.spec.ts
  • yarn format / biome lint clean on changed files

PooyaRaki and others added 2 commits May 19, 2026 18:26
Apply CaptchaGuard to GET /v1/chains/:chainId/safes/:safeAddress/positions/:fiatCode
and add integration coverage for the guard's reject/allow paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@PooyaRaki PooyaRaki marked this pull request as ready for review May 19, 2026 16:54
@PooyaRaki PooyaRaki requested a review from a team as a code owner May 19, 2026 16:54
Apply CaptchaGuard to GET /v1/portfolio/:address and add integration
coverage for the guard's reject/allow paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@PooyaRaki PooyaRaki changed the title feat(positions): protect positions endpoint with CaptchaGuard feat(positions): protect positions/portfolio endpoint with CaptchaGuard May 19, 2026
@PooyaRaki

Copy link
Copy Markdown
Contributor Author

The PR is ready to go but will move it to draft for now. We will revisit in the future.

@PooyaRaki PooyaRaki marked this pull request as draft May 21, 2026 08:20
@PooyaRaki PooyaRaki self-assigned this May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants