-
Notifications
You must be signed in to change notification settings - Fork 170
feat(api): don't strike users from /geoblock*, raise 403 #3158
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughAdds geoblocking to Compliance V2: restricted-country requests now short-circuit with 403 and an INDEXER_GEOBLOCKED_PAYLOAD (BlockedCode.GEOBLOCKED). Controller upsert logic is made geo-aware (COMPLIANT / CLOSE_ONLY / BLOCKED) and tests updated to assert 403 responses and no DB state mutations for geoblocked flows. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Client
participant Controller
participant GeoUtils
participant DB
Note over Controller: Geo-aware compliance flow
Client->>Controller: POST /api/v4/compliance/... (headers, body)
Controller->>GeoUtils: isRestrictedCountryHeaders(headers)
alt Restricted country
Controller-->>Client: 403 { INDEXER_GEOBLOCKED_PAYLOAD, code: BlockedCode.GEOBLOCKED }
Note right of Controller #aab4ff: No DB mutation or status transition
else Not restricted
Controller->>DB: read existing compliance_status
alt No existing status
Controller->>DB: upsert COMPLIANT (or BLOCKED if wallet absent rules)
else Existing status
Controller->>DB: transition per geo-aware rules (FIRST_STRIKE / FIRST_STRIKE_CLOSE_ONLY -> CLOSE_ONLY, else -> COMPLIANT)
end
Controller-->>Client: 200 ComplianceV2Response
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
e6bebfe to
3035327
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
indexer/services/comlink/src/controllers/api/v4/compliance-v2-controller.ts (1)
439-461: CRITICAL: INVALID_SURVEY from non-restricted origin incorrectly transitions to COMPLIANT.The
actionparameter is declared but never used in the non-restricted code path (lines 452-458). This causesINVALID_SURVEYfrom non-restricted origins to incorrectly transition toCOMPLIANTinstead ofCLOSE_ONLY, violating the documented behavior in the PR objectives: "INVALID_SURVEY or a restricted origin transitions to CLOSE_ONLY".Apply this diff to restore action-aware transitions:
if ( complianceStatus[0].status === ComplianceStatus.FIRST_STRIKE || complianceStatus[0].status === ComplianceStatus.FIRST_STRIKE_CLOSE_ONLY || complianceStatus[0].status === ComplianceStatus.BLOCKED || complianceStatus[0].status === ComplianceStatus.COMPLIANT ) { if (restricted) { return ComplianceStatusTable.update({ address, status: ComplianceStatus.CLOSE_ONLY, reason: getGeoComplianceReason(req.headers as GeoOriginHeaders)!, updatedAt, }); - } else { + } + + if (action === ComplianceAction.INVALID_SURVEY) { + return ComplianceStatusTable.update({ + address, + status: ComplianceStatus.CLOSE_ONLY, + updatedAt, + }); + } + - return ComplianceStatusTable.update({ - address, - status: ComplianceStatus.COMPLIANT, - updatedAt, - }); - } + return ComplianceStatusTable.update({ + address, + status: ComplianceStatus.COMPLIANT, + updatedAt, + }); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
indexer/services/comlink/__tests__/controllers/api/v4/compliance-v2-controller.test.ts(9 hunks)indexer/services/comlink/src/controllers/api/v4/compliance-v2-controller.ts(8 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
indexer/services/comlink/src/controllers/api/v4/compliance-v2-controller.ts (5)
indexer/packages/compliance/src/geoblocking/restrict-countries.ts (1)
isRestrictedCountryHeaders(6-31)indexer/packages/compliance/src/types.ts (1)
GeoOriginHeaders(8-12)indexer/services/comlink/src/lib/helpers.ts (1)
create4xxResponse(159-171)indexer/packages/compliance/src/constants.ts (1)
INDEXER_GEOBLOCKED_PAYLOAD(1-1)indexer/services/comlink/src/helpers/compliance/compliance-utils.ts (1)
getGeoComplianceReason(30-46)
indexer/services/comlink/__tests__/controllers/api/v4/compliance-v2-controller.test.ts (3)
indexer/packages/postgres/src/types/db-model-types.ts (1)
ComplianceStatusFromDatabase(248-254)indexer/packages/compliance/src/constants.ts (1)
INDEXER_GEOBLOCKED_PAYLOAD(1-1)indexer/services/comlink/__tests__/helpers/helpers.ts (1)
sendRequest(82-106)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
- GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
- GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
- GitHub Check: test / run_command
- GitHub Check: check-build-bazooka
- GitHub Check: run_command
- GitHub Check: build-and-push-mainnet
- GitHub Check: lint
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Summary
🔇 Additional comments (2)
indexer/services/comlink/src/controllers/api/v4/compliance-v2-controller.ts (1)
196-203: LGTM: Early 403 responses prevent DB writes for restricted origins.The implementation correctly returns 403 with
INDEXER_GEOBLOCKED_PAYLOADbefore any validation or database operations occur. This applies to both/geoblockand/geoblock-keplrendpoints and aligns with the PR objective to prevent DB writes and metric emissions for restricted countries.Also applies to: 249-256
indexer/services/comlink/__tests__/controllers/api/v4/compliance-v2-controller.test.ts (1)
471-719: LGTM: Comprehensive test coverage for restricted-country scenarios.The tests thoroughly verify that restricted-country requests receive 403 responses with the geoblocked payload, with no database writes or status changes occurring. Coverage includes whitelisted addresses, various existing compliance statuses, and all action types (CONNECT, INVALID_SURVEY, VALID_SURVEY).
| it.each([ | ||
| ComplianceStatus.BLOCKED, | ||
| ComplianceStatus.FIRST_STRIKE, | ||
| ComplianceStatus.FIRST_STRIKE_CLOSE_ONLY, | ||
| ])('should set status to COMPLIANT for any action from a non-restricted country with existing compliance status not CLOSE_ONLY', async (status: ComplianceStatus) => { | ||
| isRestrictedCountryHeadersSpy.mockReturnValue(false); | ||
|
|
||
| await ComplianceStatusTable.create({ | ||
| address: testConstants.defaultAddress, | ||
| status, | ||
| }); | ||
|
|
||
| const response: any = await sendRequest({ | ||
| type: RequestMethod.POST, | ||
| path: endpoint, | ||
| body, | ||
| expectedStatus: 200, | ||
| }); | ||
| expect(stats.increment).toHaveBeenCalledWith( | ||
| `${config.SERVICE_NAME}.compliance-v2-controller.${endpoint === geoblockEndpoint ? 'geo_block' : 'geo_block_keplr'}.compliance_status_changed.count`, | ||
| { | ||
| newStatus: ComplianceStatus.FIRST_STRIKE, | ||
| }); | ||
|
|
||
| expect(response.body.updatedAt).toBeDefined(); | ||
| expect(response.body.status).toEqual(ComplianceStatus.COMPLIANT); | ||
|
|
||
| const data: ComplianceStatusFromDatabase[] = await ComplianceStatusTable.findAll({}, [], {}); | ||
|
|
||
| expect(data).toHaveLength(1); | ||
| expect(data[0]).toEqual(expect.objectContaining({ | ||
| address: testConstants.defaultAddress, | ||
| status: ComplianceStatus.FIRST_STRIKE, | ||
| reason: ComplianceReason.US_GEO, | ||
| status: ComplianceStatus.COMPLIANT, | ||
| })); | ||
| }); | ||
|
|
||
| expect(response.body.status).toEqual(ComplianceStatus.FIRST_STRIKE); | ||
| expect(response.body.reason).toEqual(ComplianceReason.US_GEO); | ||
| expect(response.body.updatedAt).toBeDefined(); | ||
| it('should set status to COMPLIANT for any action from a non-restricted country with no existing compliance status', async () => { | ||
| isRestrictedCountryHeadersSpy.mockReturnValue(false); | ||
|
|
||
| const response: any = await sendRequest({ | ||
| type: RequestMethod.POST, | ||
| path: endpoint, | ||
| body, | ||
| expectedStatus: 200, | ||
| }); | ||
|
|
||
| const data: ComplianceStatusFromDatabase[] = await ComplianceStatusTable.findAll({}, [], {}); | ||
| expect(data).toHaveLength(1); | ||
| expect(data[0]).toEqual(expect.objectContaining({ | ||
| address: testConstants.defaultAddress, | ||
| status: ComplianceStatus.COMPLIANT, | ||
| })); | ||
|
|
||
| expect(response.body.status).toEqual(ComplianceStatus.COMPLIANT); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing test: INVALID_SURVEY from non-restricted origin should transition to CLOSE_ONLY.
The test suite lacks coverage for INVALID_SURVEY action from a non-restricted country. Per the PR objectives, this should transition to CLOSE_ONLY, but the current controller implementation (due to the unused action parameter bug) would incorrectly set it to COMPLIANT. Adding this test would have caught the critical issue.
Do you want me to generate a test case that verifies INVALID_SURVEY from non-restricted origins correctly transitions to CLOSE_ONLY?
🤖 Prompt for AI Agents
In
indexer/services/comlink/__tests__/controllers/api/v4/compliance-v2-controller.test.ts
around lines 721 to 770, add a test that posts an INVALID_SURVEY action from a
non-restricted country and asserts the controller persists status CLOSE_ONLY
(both in response.body.status and in the ComplianceStatusTable row), to cover
the missing case where a non-restricted INVALID_SURVEY should transition to
CLOSE_ONLY rather than COMPLIANT; set
isRestrictedCountryHeadersSpy.mockReturnValue(false), send the POST with
body.action = 'INVALID_SURVEY', expect HTTP 200, expect response.body.status ===
ComplianceStatus.CLOSE_ONLY and that ComplianceStatusTable.findAll returns one
entry with address testConstants.defaultAddress and status
ComplianceStatus.CLOSE_ONLY.
Summary
Connections from restricted origins are now immediately rejected with a **403
GeoBlocked** response and no compliance state changes.
Connections from non-restricted origins normalize any prior blocked or first-strike states to COMPLIANT, simplifying the state model.
Closed addresses are not upgraded on connection from unrestricted origins.
Changes
Restricted Origin Handling
403rejection in both/geoblockand/geoblock-keplrroutes:compliance_status_changedmetrics emitted.Non-Restricted Origin Normalization
COMPLIANT:FIRST_STRIKE→COMPLIANTFIRST_STRIKE_CLOSE_ONLY→COMPLIANTCOMPLIANT.CLOSE_ONLYandBLOCKEDstatus remains unchanged on connection from unrestricted origin.Survey Behavior
VALID_SURVEYand non-restricted origin now transitions toCOMPLIANT.INVALID_SURVEYor restricted origin transitions toCLOSE_ONLY.Tests
Updated restricted-origin test cases to:
Expect 403 with INDEXER_GEOBLOCKED_PAYLOAD
Verify no ComplianceStatusTable mutations
Confirm no metric increments for compliance_status_changed
Added coverage for non-restricted upgrades:
Verified BLOCKED, FIRST_STRIKE, and FIRST_STRIKE_CLOSE_ONLY upgrade to COMPLIANT.
Adjusted whitelisted address tests to expect 403 under restricted origins.
Deployed to internal network and testnet.
https://linear.app/dydx/issue/ENG-1111/raise-403-on-in-scope-restricted-connections-and-unban-ok-connections
Summary by CodeRabbit
New Features
Bug Fixes
Tests