rules:
  - id: auth.flow.oauth-credential-in-log
    languages:
      - javascript
      - typescript
    severity: ERROR
    message: |
      An OAuth/OIDC credential from the request flows into a logging call.
      The tainted value is an authorization `code`, an `access_token` /
      `refresh_token` / `id_token`, a bearer `token`, a `client_secret`, or
      the raw `Authorization` header, and the sink is a `console.*` or
      `logger.*` call. Logs are written to files, shipped to aggregators
      (Datadog, Splunk, CloudWatch) and read by people and systems that should
      never see live credentials. A leaked authorization code or token can be
      replayed to impersonate the user or complete the OAuth exchange
      (CWE-532).

      Never log the raw credential. Redact or mask it before logging
      (`token.slice(0, 4) + '…'`), log a non-sensitive identifier instead
      (a user id, a key id), or drop the field entirely.
    # Taint mode so indirection is caught: `const at = req.query.access_token;
    # logger.info(at)` flags, not just the inline `console.log(req.query.code)`
    # form. Distinct from auth.flow.secret-in-log (a search-mode rule keyed on
    # the NAME of the logged identifier): this is a dataflow rule keyed on the
    # request SOURCE, so it fires even when the credential is carried through an
    # arbitrarily-named intermediate variable. The source list is narrowed to
    # OAuth/OIDC credential fields (and the Authorization header), so logging a
    # benign request field such as `req.query.page` does not fire. Routing the
    # value through a redaction/masking helper or a truncating slice clears the
    # taint.
    mode: taint
    pattern-sources:
      # Dotted accessor: req.query.code / req.body.access_token / …
      - patterns:
          - pattern-either:
              - pattern: $REQ.query.$K
              - pattern: $REQ.body.$K
              - pattern: $REQ.params.$K
          - metavariable-regex:
              metavariable: $K
              regex: (?i)^(?:code|access[_-]?token|accesstoken|refresh[_-]?token|refreshtoken|id[_-]?token|idtoken|token|client[_-]?secret|clientsecret)$
      # Index accessor: req.query['id_token'] / req.body["code"] / …
      - patterns:
          - pattern-either:
              - pattern: $REQ.query[$K]
              - pattern: $REQ.body[$K]
              - pattern: $REQ.params[$K]
          - metavariable-regex:
              metavariable: $K
              regex: (?i)^["'](?:code|access[_-]?token|accesstoken|refresh[_-]?token|refreshtoken|id[_-]?token|idtoken|token|client[_-]?secret|clientsecret)["']$
      # Raw Authorization header (carries the bearer token / basic credentials).
      - pattern: $REQ.headers.authorization
      - pattern: $REQ.headers['authorization']
      - pattern: $REQ.headers["authorization"]
      - pattern: $REQ.get('authorization')
      - pattern: $REQ.get('Authorization')
      - pattern: $REQ.header('authorization')
      - pattern: $REQ.header('Authorization')
    pattern-sanitizers:
      # Redaction / masking helpers, or a truncating slice/substring: the value
      # that reaches the log is no longer the live credential.
      - pattern: redact(...)
      - pattern: mask(...)
      - pattern: maskToken(...)
      - pattern: $S.slice(...)
      - pattern: $S.substring(...)
      - pattern: $S.substr(...)
    pattern-sinks:
      # console.log/info/debug/warn/error(...): any tainted argument fires.
      - patterns:
          - pattern-either:
              - pattern: console.log(...)
              - pattern: console.info(...)
              - pattern: console.debug(...)
              - pattern: console.warn(...)
              - pattern: console.error(...)
      # logger.<level>(...): a log-named receiver with a log-level method.
      - patterns:
          - pattern: $LOG.$LEVEL(...)
          - metavariable-regex:
              metavariable: $LOG
              regex: (?i)^.*log(?:ger)?$
          - metavariable-regex:
              metavariable: $LEVEL
              regex: ^(?:log|info|debug|warn|warning|error|trace|fatal|verbose|silly)$
    metadata:
      oauthlint-rule-id: AUTH-FLOW-013
      oauthlint-doc-url: https://oauthlint.dev/rules/flow-oauth-credential-in-log
      category: security
      cwe: CWE-532
      owasp: API8:2023
      llm-prevalence: MEDIUM
      technology:
        - express
      references:
        - https://cwe.mitre.org/data/definitions/532.html
        - https://datatracker.ietf.org/doc/html/rfc6749#section-10.3
        - https://owasp.org/API-Security/editions/2023/en/0xa8-security-misconfiguration/
