rules:
  - id: auth.nextauth.hardcoded-secret
    languages:
      - javascript
      - typescript
    severity: ERROR
    message: |
      The NextAuth/Auth.js `secret` is set to a hard-coded string literal.

      This value signs and encrypts every session JWT and CSRF token. Committed
      to git it is one search away from compromise, letting an attacker forge
      sessions for any user. Read it from the environment instead:
      `secret: process.env.AUTH_SECRET` (or `NEXTAUTH_SECRET`) and add the
      variable to `.env.example` with a placeholder.
    # Anchored to a NextAuth/Auth.js config so a bare `secret:` in unrelated
    # code never fires: the property must sit inside a `NextAuth(...)` /
    # `Auth(...)` call, an `authOptions`/`authConfig` object, or an object typed
    # as `NextAuthOptions` / `NextAuthConfig` / `AuthOptions`. The value is an
    # AST string literal (`"..."`), so `process.env.*` reads are structurally
    # excluded; the regex allow-list drops `${ENV}` templates, `<placeholders>`,
    # and obvious doc/test stubs. `paths.exclude` keeps the rule off test and
    # example trees (where dev secrets like `"secret"` are intentional), while
    # still firing on its own fixtures under rules/tests/fixtures/.
    patterns:
      - pattern: 'secret: "..."'
      - pattern-not-regex: |-
          (?i)secret\s*:\s*['"]\$\{?[A-Za-z_]+\}?['"]
      - pattern-not-regex: |-
          (?i)secret\s*:\s*['"]<[^'"]*>['"]
      - pattern-not-regex: |-
          (?i)secret\s*:\s*['"](?:your[-_]|my[-_]|example|placeholder|xxx+|todo|fixme|test|dummy|fake|sample|changeme|change[-_]?me|redacted|replace)
      - pattern-either:
          - pattern-inside: 'NextAuth({...})'
          - pattern-inside: 'NextAuth($A, {...})'
          - pattern-inside: 'NextAuth($A, $B, {...})'
          - pattern-inside: 'Auth($A, {...})'
          - pattern-inside: 'authOptions = {...}'
          - pattern-inside: 'authConfig = {...}'
          - pattern-inside: '$X: NextAuthOptions = {...}'
          - pattern-inside: '$X: NextAuthConfig = {...}'
          - pattern-inside: '$X: AuthOptions = {...}'
    paths:
      exclude:
        - "**/test/**"
        - "**/__tests__/**"
        - "**/*.test.*"
        - "**/*.spec.*"
        - "**/example/**"
        - "**/examples/**"
        - "**/demo/**"
    metadata:
      oauthlint-rule-id: AUTH-NEXTAUTH-001
      oauthlint-doc-url: https://oauthlint.dev/rules/nextauth-hardcoded-secret
      category: security
      cwe: CWE-798
      owasp: API8:2023
      llm-prevalence: HIGH
      technology:
        - next-auth
      references:
        - https://authjs.dev/getting-started/deployment#auth_secret
        - https://cwe.mitre.org/data/definitions/798.html
