rules:
  - id: auth.flow.insecure-random
    languages:
      - javascript
      - typescript
    severity: ERROR
    message: |
      `Math.random()` produces a value whose name marks it as
      security-sensitive. The value is assigned to (or computed for) a token,
      a CSRF value, an OAuth `state`, a session id, a nonce, or a
      verification code. `Math.random()` is NOT cryptographically secure and
      is predictable enough for an attacker with enough samples to recover
      the seed.

      Use `crypto.randomBytes(N)` (Node) or `crypto.getRandomValues()`
      (browser) and base64url-encode the result.

      OWASP ASVS V2.5: all security-sensitive random values must come
      from a CSPRNG.
    # `<... Math.random() ...>` deep-matches Math.random() anywhere in the
    # initializer, covering the common token idioms:
    #   Math.random().toString(36).slice(2) / .substring(2) / Math.floor(Math.random()*1e9)
    # The name regex is case-insensitive (catches resetToken, csrfToken, …) and
    # uses specific security words only. Bare `code`/`sid` were removed because
    # they matched barcode/zipcode/aside and produced false positives.
    pattern-either:
      - patterns:
          - pattern: 'const $NAME = <... Math.random() ...>'
          - metavariable-regex:
              metavariable: $NAME
              regex: (?i)^(?:[a-z][a-z0-9_]*)?(?:token|secret|csrf|nonce|sessionid|otp|verificationcode|verifycode|resetcode|apikey|password|salt)[a-z0-9_]*$
      - patterns:
          - pattern: 'let $NAME = <... Math.random() ...>'
          - metavariable-regex:
              metavariable: $NAME
              regex: (?i)^(?:[a-z][a-z0-9_]*)?(?:token|secret|csrf|nonce|sessionid|otp|verificationcode|verifycode|resetcode|apikey|password|salt)[a-z0-9_]*$
      - patterns:
          - pattern: 'var $NAME = <... Math.random() ...>'
          - metavariable-regex:
              metavariable: $NAME
              regex: (?i)^(?:[a-z][a-z0-9_]*)?(?:token|secret|csrf|nonce|sessionid|otp|verificationcode|verifycode|resetcode|apikey|password|salt)[a-z0-9_]*$
      - patterns:
          - pattern: '$OBJ.$NAME = <... Math.random() ...>'
          - metavariable-regex:
              metavariable: $NAME
              regex: (?i)^(?:[a-z][a-z0-9_]*)?(?:token|secret|csrf|nonce|sessionid|otp|verificationcode|verifycode|resetcode|apikey|password|salt)[a-z0-9_]*$
    metadata:
      oauthlint-rule-id: AUTH-FLOW-003
      oauthlint-doc-url: https://oauthlint.dev/rules/flow-insecure-random
      category: security
      cwe: CWE-338
      owasp: API2:2023
      llm-prevalence: HIGH
      references:
        - https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html
        - https://cwe.mitre.org/data/definitions/338.html
