Skip to content

SEVERE: No Rate Limiting on Public Endpoints #33

@coderabbitai

Description

@coderabbitai

Security Issue

Severity: Severe
Related PR: #30

Description

All API endpoints lack rate limiting, making the system vulnerable to abuse and denial of service attacks.

Vulnerable Endpoints

High-Risk (resource-intensive):

  • POST /api/kestra/trigger - Triggers full security scans
  • POST /webhook/task-update - State manipulation
  • POST /webhook/execution-update - State manipulation
  • /ws/execution/{execution_id} - WebSocket connections

Medium-Risk:

  • GET /api/kestra/execution/{execution_id}
  • GET /api/kestra/execution/{execution_id}/logs
  • GET /webhook/status/{execution_id}

Attack Scenarios

  1. Resource Exhaustion:
# Trigger 1000 concurrent scans
for i in {1..1000}; do
  curl -X POST http://api/kestra/trigger \
    -H "Content-Type: application/json" \
    -d '{"repository_url": "https://github.com/large/repo"}' &
done
  1. WebSocket Flooding:
// Open thousands of WebSocket connections
for (let i = 0; i < 10000; i++) {
  new WebSocket('ws://api/ws/execution/fake-id');
}
  1. State Manipulation Storm:
# Spam fake webhook updates
while true; do
  curl -X POST http://api/webhook/execution-update \
    -d '{"execution_id":"x","state":"FAILED"}'
done

Security Impact

  • Denial of Service: Overwhelming the backend and Kestra services
  • Cost Escalation: Abuse of cloud resources and API quotas (Gemini)
  • Service Degradation: Legitimate users unable to access system
  • Data Corruption: Flood of fake webhook updates corrupting state

Remediation

  1. Install Rate Limiting Middleware:
# requirements.txt
slowapi==0.1.9
  1. Apply Rate Limits:
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded

limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)

# On expensive endpoints
@router.post("/kestra/trigger")
@limiter.limit("5/minute")  # Max 5 scans per minute per IP
async def trigger_kestra_flow(request: Request, req: KestraFlowRequest):
    ...

# On webhooks
@webhook_router.post("/task-update")
@limiter.limit("100/minute")
async def receive_task_update(request: Request):
    ...

# On WebSocket
@app.websocket("/ws/execution/{execution_id}")
@limiter.limit("10/minute")
async def websocket_endpoint(websocket: WebSocket, execution_id: str):
    ...
  1. Per-User Rate Limits (after authentication added):
def get_user_id(request: Request):
    # Extract from JWT/API key
    return request.headers.get("X-User-ID", get_remote_address(request))

limiter = Limiter(key_func=get_user_id)
  1. Configure Redis for Distributed Rate Limiting:
from slowapi.util import get_remote_address
import redis

redis_client = redis.Redis(host='redis', port=6379)
limiter = Limiter(
    key_func=get_remote_address,
    storage_uri="redis://redis:6379"
)

Recommended Limits

  • Scan triggers: 5/minute, 20/hour per IP/user
  • Webhooks: 100/minute per IP (legitimate Kestra)
  • WebSocket connections: 10/minute, 5 concurrent per IP
  • Status checks: 60/minute per IP

Assignee: @haroon0x

Metadata

Metadata

Assignees

Labels

securitySecurity related issues

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions