rules:
  - id: auth.flow.basic-auth-in-log
    languages:
      - javascript
      - typescript
    severity: ERROR
    message: |
      HTTP Basic credentials flow into a logging call (`console.*` or
      `logger.*`). The `basic-auth` package returns the decoded `{ name, pass }`
      for the request, and the `Proxy-Authorization` header carries
      base64-encoded `user:password` just like `Authorization` does. Logs are
      written to files, shipped to aggregators (Datadog, Splunk, CloudWatch) and
      read by people and systems that should never see live credentials. The
      base64 is trivially reversible, so a logged Basic header or a logged
      `creds.pass` is a plaintext password leak (CWE-532).

      Never log the raw credential. Redact or mask it before logging, log a
      non-sensitive identifier instead (the username alone, a user id), or drop
      the field entirely.
    # Taint mode so indirection is caught: `const creds = auth(req);
    # logger.info(creds.pass)` flags, not just an inline log.
    #
    # SCOPE / non-duplication: auth.flow.oauth-credential-in-log already covers
    # the raw `Authorization` header (`req.headers.authorization`,
    # `req.get('authorization')`, `req.header('authorization')`) flowing into a
    # log, so this rule deliberately does NOT re-list those sources. It adds the
    # genuinely-uncovered Basic-auth sources: the `basic-auth` package's parse
    # result (`auth(req)` / `basicAuth(req)`, incl. `.name` / `.pass`) and the
    # `Proxy-Authorization` header. Routing the value through a redaction /
    # masking helper or a truncating slice clears the taint.
    mode: taint
    pattern-sources:
      # `basic-auth` package: const credentials = auth(req)  ->  { name, pass }.
      # $REQ is constrained to conventional request-object names so an unrelated
      # `auth(user)` / `auth(config)` call is not treated as a Basic-auth parse.
      - patterns:
          - pattern-either:
              - pattern: auth($REQ)
              - pattern: basicAuth($REQ)
              - pattern: auth($REQ).pass
              - pattern: auth($REQ).name
              - pattern: basicAuth($REQ).pass
              - pattern: basicAuth($REQ).name
          - metavariable-regex:
              metavariable: $REQ
              regex: ^(req|request|ctx|context|c|r)$
      # Proxy-Authorization header (base64 user:password, like Authorization).
      - pattern: $REQ.headers['proxy-authorization']
      - pattern: $REQ.headers["proxy-authorization"]
      - pattern: $REQ.get('proxy-authorization')
      - pattern: $REQ.get('Proxy-Authorization')
      - pattern: $REQ.header('proxy-authorization')
      - pattern: $REQ.header('Proxy-Authorization')
    pattern-sanitizers:
      # The username is not a secret: reading `.name` / `.username` off the
      # decoded Basic credentials clears the taint, so logging only the username
      # (`creds.name`) does not fire: only the password / raw header does.
      - pattern: $C.name
      - pattern: $C.username
      # 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-014
      oauthlint-doc-url: https://oauthlint.dev/rules/flow-basic-auth-in-log
      category: security
      cwe: CWE-532
      owasp: API8:2023
      llm-prevalence: MEDIUM
      technology:
        - express
        - basic-auth
      references:
        - https://cwe.mitre.org/data/definitions/532.html
        - https://www.npmjs.com/package/basic-auth
        - https://owasp.org/API-Security/editions/2023/en/0xa8-security-misconfiguration/
