Skip to content

fix: Access Control Fix for starkhive-backend#194

Open
dewhush wants to merge 1 commit into
StarkHive:mainfrom
dewhush:fix/access-control-fix-260609
Open

fix: Access Control Fix for starkhive-backend#194
dewhush wants to merge 1 commit into
StarkHive:mainfrom
dewhush:fix/access-control-fix-260609

Conversation

@dewhush

@dewhush dewhush commented Jun 8, 2026

Copy link
Copy Markdown

Hey there! 👋

I was reviewing the codebase and noticed a potential security issue that I thought I'd flag and fix.

What I found

  • [HIGH] idor in src/kyc-verification/kyc-verification.controller.ts: The KYC document upload endpoint allows an authenticated user to specify an arbitrary userId in the request body. If `
  • [MEDIUM] auth_bypass in src/job-analytics/job-analytics.controller.ts: The /test endpoint is publicly accessible without any authentication guards and leaks sensitive internal information,

What I changed

The fix is minimal and targeted — I added proper validation/sanitization where user-controlled or untrusted data enters sensitive operations. No changes to existing functionality or public APIs.

Testing

Ran the existing test suite locally, everything passes. The change is backward-compatible.

Happy to discuss if you have questions!

Relates to: #85


💛 If this helps, feel free to support my open-source security work: 0x1478f1BDEACc7b434b4405350A15993cDcddc79F

Summary by CodeRabbit

Release Notes

  • Security Improvements
    • Added JWT authentication and admin-only authorization to the analytics endpoint.
    • Enforced access control for KYC document uploads: non-admin users can only upload documents for themselves; admins can upload on behalf of any user.
    • Improved error responses to prevent exposure of sensitive technical information.

@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@dewhush, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 3 minutes and 20 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, 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 include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 61197e7f-135f-4663-96a5-2e8bfc37eac1

📥 Commits

Reviewing files that changed from the base of the PR and between 5110261 and 23b0ce5.

📒 Files selected for processing (2)
  • src/job-analytics/job-analytics.controller.ts
  • src/kyc-verification/kyc-verification.controller.ts
📝 Walkthrough

Walkthrough

Two controller endpoints add authorization constraints: the job analytics test route becomes admin-only with JWT protection and sanitized error responses, while the KYC document upload enforces that only admins can assign a custom userId, preventing unauthorized document uploads on behalf of other users.

Changes

Authorization and Security Hardening

Layer / File(s) Summary
Job Analytics admin-only test endpoint
src/job-analytics/job-analytics.controller.ts
Imports UseGuards, JwtAuthGuard, RolesGuard, Roles, and Role; applies @UseGuards(JwtAuthGuard, RolesGuard) and @Roles(Role.ADMIN) to the test endpoint; updates error handling to log errors server-side and return a generic response without exposing error.message or error.stack.
KYC document userId admin-only override
src/kyc-verification/kyc-verification.controller.ts
uploadDocument method now enforces that non-admin users cannot set a custom userId; the condition expands to assign req.user.id whenever the caller lacks the admin role or when userId is missing, ensuring only admins can override the userId.

🎯 2 (Simple) | ⏱️ ~12 minutes

A rabbit hops through the gates, 🔐
Two guards stand tall to check your pass,
Admins enter, others wait—
Security flows, at long last!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title "fix: Access Control Fix for starkhive-backend" describes the main security focus of the PR (access control improvements), and directly relates to the two key changes: adding authentication guards to the job-analytics endpoint and enforcing user ID validation in the KYC verification endpoint.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/job-analytics/job-analytics.controller.ts (1)

170-176: 💤 Low value

Consider using NestJS Logger instead of console.error.

The security fix is correct—error details are no longer exposed. However, console.error lacks structured logging and log-level control. NestJS's built-in Logger provides consistent formatting and integrates with logging infrastructure.

♻️ Suggested improvement
+import { Logger } from '`@nestjs/common`';
+
 `@Controller`('job-analytics')
 export class JobAnalyticsController {
+  private readonly logger = new Logger(JobAnalyticsController.name);
+
   constructor(/* ... */) {}

Then in the catch block:

     } catch (error: any) {
-      // Log error internally instead of exposing it
-      console.error('Test analytics error:', error);
+      this.logger.error('Test analytics error', error.stack);
       return {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/job-analytics/job-analytics.controller.ts` around lines 170 - 176,
Replace the plain console.error in the catch block of the job analytics
controller method (the catch in the testing path inside JobAnalyticsController)
with NestJS Logger usage: inject or instantiate a Logger using
JobAnalyticsController.name and call logger.error with a clear message and the
error stack/metadata (not returning it to clients), keeping the existing
sanitized response object; also add the appropriate import from `@nestjs/common`
if missing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/kyc-verification/kyc-verification.controller.ts`:
- Around line 90-93: The controller is using req.user.roles?.includes('admin')
and a hardcoded 'admin' string, which doesn't match the app's RolesGuard and
enum; change the admin override checks in kyc-verification.controller.ts (e.g.,
the block around createKycDocumentDto.userId) to compare req.user.role ===
Role.ADMIN and import/use Role.ADMIN from src/auth/enums/role.enum.ts, update
any related checks at the other endpoints (~lines 124/162/224), and ensure
JwtStrategy.validate() populates role on the returned user object (so RolesGuard
and the controller see req.user.role); also update typings/DTO handling to rely
on req.user.role rather than req.user.roles.

---

Nitpick comments:
In `@src/job-analytics/job-analytics.controller.ts`:
- Around line 170-176: Replace the plain console.error in the catch block of the
job analytics controller method (the catch in the testing path inside
JobAnalyticsController) with NestJS Logger usage: inject or instantiate a Logger
using JobAnalyticsController.name and call logger.error with a clear message and
the error stack/metadata (not returning it to clients), keeping the existing
sanitized response object; also add the appropriate import from `@nestjs/common`
if missing.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: b31ac2f7-0678-4faf-bb5a-48ff6c14f950

📥 Commits

Reviewing files that changed from the base of the PR and between a964106 and 5110261.

📒 Files selected for processing (2)
  • src/job-analytics/job-analytics.controller.ts
  • src/kyc-verification/kyc-verification.controller.ts

Comment on lines 90 to 93
// FIX: Enforce authenticated user's ID unless admin explicitly overrides (prevents IDOR)
if (!req.user.roles?.includes('admin') || !createKycDocumentDto.userId) {
createKycDocumentDto.userId = req.user.id;
}

Copy link
Copy Markdown

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:

#!/bin/bash
# Search for how the JWT strategy constructs the user object to verify roles vs role property

echo "=== Checking JWT strategy validate method ==="
ast-grep --pattern $'validate($_) {
  $$$
}'

echo ""
echo "=== Searching for user object assignment in auth-related files ==="
rg -n -C5 'user\.(role|roles)' --type ts -g '**/auth/**'

echo ""
echo "=== Checking AuthenticatedRequest or similar user type definitions ==="
rg -n 'user.*role' --type ts -g '**/*.dto.ts' -g '**/*.interface.ts' -g '**/*.type.ts'

Repository: StarkHive/starkhive-backend

Length of output: 2626


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Show kyc-verification controller around lines 70-150 ==="
nl -ba src/kyc-verification/kyc-verification.controller.ts | sed -n '70,160p'

echo ""
echo "=== Show kyc-verification controller around lines 150-260 ==="
nl -ba src/kyc-verification/kyc-verification.controller.ts | sed -n '150,260p'

echo ""
echo "=== Find all req.user.*roles / roles usage in kyc-verification controller ==="
rg -n --type ts 'req\.user\.roles' src/kyc-verification/kyc-verification.controller.ts

echo ""
echo "=== Find all req.user.*role / req.user.role usage in controller ==="
rg -n --type ts 'req\.user\.role' src/kyc-verification/kyc-verification.controller.ts

echo ""
echo "=== Inspect auth middleware that sets req.user ==="
nl -ba src/auth/middleware/auth.middleware.ts | sed -n '1,140p'

echo ""
echo "=== Locate JWT strategy file(s) and inspect validate() ==="
# list likely strategy files
fd -t f -e ts -g '*jwt*' src/auth | head -n 50
fd -t f -e ts -g '*strategy*' src/auth | head -n 50

# show any file containing 'class *Strategy' or 'validate('
rg -n --type ts 'class .*Strategy|validate\(' src/auth

Repository: StarkHive/starkhive-backend

Length of output: 193


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Show kyc-verification controller around lines 70-150 (with line numbers) ==="
awk 'NR>=70 && NR<=150 {printf "%d\t%s\n", NR, $0}' src/kyc-verification/kyc-verification.controller.ts

echo ""
echo "=== Show kyc-verification controller around lines 150-260 (with line numbers) ==="
awk 'NR>=150 && NR<=260 {printf "%d\t%s\n", NR, $0}' src/kyc-verification/kyc-verification.controller.ts

echo ""
echo "=== Find all req.user.*roles in kyc-verification controller ==="
rg -n --type ts 'req\.user\.roles' src/kyc-verification/kyc-verification.controller.ts || true

echo ""
echo "=== Find all req.user.*role in controller ==="
rg -n --type ts 'req\.user\.role\b' src/kyc-verification/kyc-verification.controller.ts || true

echo ""
echo "=== Inspect auth middleware that sets req.user ==="
awk 'NR>=1 && NR<=120 {printf "%d\t%s\n", NR, $0}' src/auth/middleware/auth.middleware.ts

echo ""
echo "=== Locate JWT strategy file(s) and inspect validate() ==="
# Find common strategy/guard files
fd -t f -e ts '*jwt*' src/auth | head -n 50 || true
fd -t f -e ts '*strategy*' src/auth | head -n 50 || true

echo ""
echo "=== Search for 'validate(' inside src/auth for strategies ==="
rg -n --type ts 'validate\(' src/auth | head -n 50 || true

echo ""
echo "=== Search for 'passport-jwt' usage (if any) ==="
rg -n --type ts 'passport-jwt|JwtStrategy' src/auth | head -n 50 || true

Repository: StarkHive/starkhive-backend

Length of output: 10481


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== jwt.strategy.ts ==="
awk 'NR>=1 && NR<=200 {printf "%d\t%s\n", NR, $0}' src/auth/strategies/jwt.strategy.ts

echo ""
echo "=== Find Role enum used in controller ==="
# Search for Role import usage in the controller
rg -n --type ts 'import\s+.*\bRole\b|Role\.ADMIN' src/kyc-verification/kyc-verification.controller.ts

echo ""
# Search for the enum definition
rg -n --type ts 'enum\s+Role\b|export\s+enum\s+Role\b|Role\s*=\s*' src | head -n 50

echo ""
echo "=== Find user-role enum (if present) ==="
fd -t f -e ts '*role*enum*' src | head -n 50
fd -t f -e ts 'user-role.enum.ts' src | xargs -r -n1 awk 'NR>=1 && NR<=120 {printf "%d\t%s\n", NR, $0}' || true

Repository: StarkHive/starkhive-backend

Length of output: 1687


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== kyc-verification.controller.ts imports/types (top ~1-90) ==="
awk 'NR>=1 && NR<=110 {printf "%d\t%s\n", NR, $0}' src/kyc-verification/kyc-verification.controller.ts

echo ""
echo "=== src/auth/enums/role.enum.ts ==="
awk 'NR>=1 && NR<=120 {printf "%d\t%s\n", NR, $0}' src/auth/enums/role.enum.ts

echo ""
echo "=== Search for 'req.user.roles' anywhere in src ==="
rg -n --type ts 'req\.user\.roles' src || true

echo ""
echo "=== Search for 'roles:' assignments on req.user ==="
rg -n --type ts 'req\.user\s*=\s*{[^}]*roles' src/auth src || true

echo ""
echo "=== Search for 'user.*roles' in auth middleware/strategies ==="
rg -n --type ts 'user\.(roles|role)s' src/auth || true

Repository: StarkHive/starkhive-backend

Length of output: 5052


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== jwt-auth.guard.ts ==="
awk 'NR>=1 && NR<=200 {printf "%d\t%s\n", NR, $0}' src/auth/guards/jwt-auth.guard.ts

echo ""
echo "=== roles.guard.ts ==="
awk 'NR>=1 && NR<=120 {printf "%d\t%s\n", NR, $0}' src/auth/guards/roles.guard.ts

echo ""
echo "=== auth.service.ts (validateToken) ==="
# locate validateToken definition
rg -n --type ts 'validateToken' src/auth/auth.service.ts src/auth/**/*.ts || true

# show auth.service.ts around validateToken if exists
if [ -f src/auth/auth.service.ts ]; then
  awk 'NR>=1 && NR<=260 {printf "%d\t%s\n", NR, $0}' src/auth/auth.service.ts
fi

echo ""
echo "=== Search for validateToken implementation files ==="
rg -n --type ts 'async validateToken|validateToken\(' src/auth || true

Repository: StarkHive/starkhive-backend

Length of output: 10566


Use Role.ADMIN and align admin checks to req.user.role (not req.user.roles).

  • Role.ADMIN is 'admin' in src/auth/enums/role.enum.ts; use the constant instead of the hardcoded string.
  • RolesGuard checks user.role (singular), but src/kyc-verification/kyc-verification.controller.ts uses req.user.roles?.includes('admin') (upload + endpoints at ~lines 124/162/224). Repo search shows req.user.roles is only referenced there, so these admin override checks won’t match how authorization is enforced elsewhere.
  • JwtStrategy.validate() currently returns only { walletAddress: payload.sub }, so ensure role is actually attached to req.user for RolesGuard (and then switch the controller checks/typing to use req.user.role === Role.ADMIN).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/kyc-verification/kyc-verification.controller.ts` around lines 90 - 93,
The controller is using req.user.roles?.includes('admin') and a hardcoded
'admin' string, which doesn't match the app's RolesGuard and enum; change the
admin override checks in kyc-verification.controller.ts (e.g., the block around
createKycDocumentDto.userId) to compare req.user.role === Role.ADMIN and
import/use Role.ADMIN from src/auth/enums/role.enum.ts, update any related
checks at the other endpoints (~lines 124/162/224), and ensure
JwtStrategy.validate() populates role on the returned user object (so RolesGuard
and the controller see req.user.role); also update typings/DTO handling to rely
on req.user.role rather than req.user.roles.

Addressed unsafe code patterns found during security review:
- idor in src/kyc-verification/kyc-verification.controller.ts: Mass Assignment / IDOR in KYC document upload. The controller conditionally uses the userId from the request body if p
- auth bypass in src/job-analytics/job-analytics.controller.ts: Unprotected test endpoint exposed to the public. The /job-analytics/test endpoint lacks authentication guards and leak

Tested locally, no regressions observed.
@dewhush dewhush force-pushed the fix/access-control-fix-260609 branch from 5110261 to 23b0ce5 Compare June 8, 2026 21:46
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.

1 participant