rules:
  - id: auth.oauth.broad-scope
    languages:
      - javascript
      - typescript
    severity: INFO
    message: |
      OAuth scope request includes an over-broad scope such as `admin`,
      `full_access`, `*`, or `repo` (entire GitHub access). LLMs default
      to the widest scope that "works", but every extra scope expands
      the blast radius if the access token is leaked or replayed.

      Request the narrowest scope that satisfies your feature. Examples:
      use `repo:status` instead of `repo`, `gmail.send` instead of
      `https://mail.google.com/`, and scope down to `read:user` when you
      only need a profile.
    # An over-broad scope must be a STANDALONE token (bounded by the quote, a
    # space, or a comma, since OAuth scopes are space-delimited). Structured
    # sub-scopes such as `admin:read`, `repo:status`, `public_repo`,
    # `channels:admin`, `calendar.all` are narrower and intentionally NOT
    # flagged (they previously matched via `\b` and produced false positives).
    pattern-either:
      - pattern-regex: |-
              scope\s*[:=]\s*(['"])(?:[^'"]*[ ,])?(?:admin|full_access|all|root|superuser|repo)(?:[ ,][^'"]*)?\1
      - pattern-regex: |-
              scope\s*[:=]\s*['"][^'"]*\*[^'"]*['"]
      - pattern-regex: |-
              scope\s*[:=]\s*['"][^'"]*https://mail\.google\.com/[^'"]*['"]
      - pattern-regex: |-
              scopes?\s*[:=]\s*\[[^\]]*(['"])(?:[^'"]*[ ,])?(?:admin|full_access|all|root|superuser|repo)(?:[ ,][^'"]*)?\1[^\]]*\]
    metadata:
      oauthlint-rule-id: AUTH-OAUTH-006
      oauthlint-doc-url: https://oauthlint.dev/rules/oauth-broad-scope
      category: security
      cwe: CWE-272
      owasp: API1:2023
      llm-prevalence: HIGH
      references:
        - https://datatracker.ietf.org/doc/html/rfc6749#section-3.3
