rules:
  - id: auth.flow.no-rate-limit
    languages:
      - javascript
      - typescript
    severity: INFO
    message: |
      A `/login`, `/signin`, `/auth`, or `/reset-password` POST handler is
      registered without any rate-limit middleware in scope. Without a
      rate limit, credential-stuffing and brute-force attacks are
      essentially free for the attacker.

      Add `express-rate-limit`, `@fastify/rate-limit`, or a gateway-level
      WAF rule. Per-IP + per-account is the typical pairing.
    # 2-argument route registration (path, handler) on any router-like object
    # (app / router / fastify / …) whose path looks like an auth endpoint. The
    # 2-arg form means no middleware is in the chain; adding rate-limit
    # middleware uses the 3+-arg form (or fastify's options object) and so is
    # not flagged. Path matching is a regex, so `/api/login`, `/v1/auth`,
    # `/auth/signin` etc. are all covered.
    # NOTE: app-level rate limiting (`app.use(rateLimit())`) is invisible to a
    # purely syntactic rule, hence INFO severity.
    patterns:
      - pattern: '$APP.post($PATH, $HANDLER)'
      - metavariable-regex:
          metavariable: $PATH
          regex: ^['"][^'"]*\/(?:login|signin|sign-in|signup|sign-up|authenticate|auth|reset-password|forgot-password)(?![a-z-])[^'"]*['"]$
    metadata:
      oauthlint-rule-id: AUTH-FLOW-002
      oauthlint-doc-url: https://oauthlint.dev/rules/flow-no-rate-limit
      category: security
      cwe: CWE-307
      owasp: API4:2023
      llm-prevalence: HIGH
      technology:
        - express
        - fastify
      references:
        - https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
