rules:
  - id: auth.flow.secret-in-log
    languages:
      - javascript
      - typescript
    severity: WARNING
    message: |
      A secret-shaped value is passed to a logging call. The logged
      identifier is named like a credential (`password`, `token`, `secret`,
      `apiKey`, `accessToken`, `refreshToken`, `privateKey`, `clientSecret`,
      …) and the sink is a `console.*` or `logger.*` call. Logs are routinely
      written to files, shipped to aggregators (Datadog, Splunk, CloudWatch)
      and read by people who should never see the raw secret. This is a
      textbook credential leak.

      Never log secrets. Redact or mask them before logging
      (`token.slice(0, 4) + '…'`), log a non-sensitive identifier instead
      (a user id, a key id), or drop the field entirely.
    # FP control: we only fire when a logging call receives an *identifier*
    # whose name ends with a secret word. The argument may sit at any position
    # (`$...A, $SECRET, $...B`). The `metavariable-pattern` with a bare-identifier
    # `pattern-regex` forces `$SECRET` to be an identifier (or, in the template
    # arms, an interpolated identifier), NOT a string literal or member access,
    # so status messages like `console.log('password updated')`,
    # `console.log('reset token sent')` and `console.log(user.id)` are excluded.
    # The outer `metavariable-regex` then requires the identifier name itself to
    # match a secret word (re-using the suffix list from
    # auth.flow.timing-unsafe-compare).
    pattern-either:
      # console.log/info/debug/warn/error(... secretVar ...): secret at any position
      - patterns:
          - pattern-either:
              - pattern: console.log($...A, $SECRET, $...B)
              - pattern: console.info($...A, $SECRET, $...B)
              - pattern: console.debug($...A, $SECRET, $...B)
              - pattern: console.warn($...A, $SECRET, $...B)
              - pattern: console.error($...A, $SECRET, $...B)
          - metavariable-pattern:
              metavariable: $SECRET
              pattern-regex: ^[A-Za-z_$][A-Za-z0-9_$]*$
          - metavariable-regex:
              metavariable: $SECRET
              regex: (?i)^(?:password|passwd|pwd|secret|token|apikey|api_key|accesstoken|refreshtoken|privatekey|clientsecret)$
      # logger.<level>(... secretVar ...)
      - patterns:
          - pattern: $LOG.$LEVEL($...A, $SECRET, $...B)
          - metavariable-regex:
              metavariable: $LOG
              regex: (?i)^.*log(?:ger)?$
          - metavariable-regex:
              metavariable: $LEVEL
              regex: ^(?:log|info|debug|warn|warning|error|trace|fatal|verbose|silly)$
          - metavariable-pattern:
              metavariable: $SECRET
              pattern-regex: ^[A-Za-z_$][A-Za-z0-9_$]*$
          - metavariable-regex:
              metavariable: $SECRET
              regex: (?i)^(?:password|passwd|pwd|secret|token|apikey|api_key|accesstoken|refreshtoken|privatekey|clientsecret)$
      # Template-literal interpolation: console.log(`token=${token}`)
      - patterns:
          - pattern-either:
              - pattern: console.log(`...${$SECRET}...`)
              - pattern: console.info(`...${$SECRET}...`)
              - pattern: console.debug(`...${$SECRET}...`)
              - pattern: console.warn(`...${$SECRET}...`)
              - pattern: console.error(`...${$SECRET}...`)
          - metavariable-pattern:
              metavariable: $SECRET
              pattern-regex: ^[A-Za-z_$][A-Za-z0-9_$]*$
          - metavariable-regex:
              metavariable: $SECRET
              regex: (?i)^(?:password|passwd|pwd|secret|token|apikey|api_key|accesstoken|refreshtoken|privatekey|clientsecret)$
      - patterns:
          - pattern: $LOG.$LEVEL(`...${$SECRET}...`)
          - metavariable-regex:
              metavariable: $LOG
              regex: (?i)^.*log(?:ger)?$
          - metavariable-regex:
              metavariable: $LEVEL
              regex: ^(?:log|info|debug|warn|warning|error|trace|fatal|verbose|silly)$
          - metavariable-pattern:
              metavariable: $SECRET
              pattern-regex: ^[A-Za-z_$][A-Za-z0-9_$]*$
          - metavariable-regex:
              metavariable: $SECRET
              regex: (?i)^(?:password|passwd|pwd|secret|token|apikey|api_key|accesstoken|refreshtoken|privatekey|clientsecret)$
    metadata:
      oauthlint-rule-id: AUTH-FLOW-008
      oauthlint-doc-url: https://oauthlint.dev/rules/flow-secret-in-log
      category: security
      cwe: CWE-532
      owasp: API8:2023
      llm-prevalence: HIGH
      references:
        - https://cwe.mitre.org/data/definitions/532.html
        - https://owasp.org/API-Security/editions/2023/en/0xa8-security-misconfiguration/
