{"record":{"id":"dde5d2aea1d50867","repo":"wpscanteam/wpscan","slug":"saml-authentication-needs-an-interactive-terminal","errorCode":null,"errorMessage":"SAML authentication needs an interactive terminal to wait for login, but stdin is not a TTY. Run wpscan from a real shell when using --expect-saml.","messagePattern":"SAML authentication needs an interactive terminal to wait for login, but stdin is not a TTY\\. Run wpscan from a real shell when using --expect-saml\\.","errorType":"exception","errorClass":"WPScan::Error::BrowserFailed","httpStatus":null,"severity":"error","filePath":"lib/wpscan/browser_authenticator.rb","lineNumber":14,"sourceCode":"# frozen_string_literal: true\n\nrequire 'ferrum'\n\nmodule WPScan\n  module BrowserAuthenticator\n    # Characters that, if present in a cookie name or value, would corrupt the\n    # serialized Cookie header. Per RFC 6265 these are forbidden in cookie-octets,\n    # but a noncompliant IdP could still emit them.\n    COOKIE_DELIMITERS = /[;,\\s]/\n\n    def self.authenticate(login_url)\n      unless $stdin.tty?\n        raise WPScan::Error::BrowserFailed,\n              'SAML authentication needs an interactive terminal to wait for login, but stdin is not a TTY. ' \\\n              'Run wpscan from a real shell when using --expect-saml.'\n      end\n\n      cookies = run_login_session(login_url)\n\n      raise WPScan::Error::SAMLAuthenticationFailed if cookies.nil? || cookies.empty?\n\n      serialize_cookies(cookies)\n    end\n\n    # Drives the interactive browser session and returns the resulting cookie jar.\n    # Translates Ferrum failures into BrowserFailed with a context-specific message.\n    def self.run_login_session(login_url)\n      browser = Ferrum::Browser.new(headless: false)\n\n      puts 'SAML authentication needed. Log in via the browser window that just opened, then press enter.'\n      browser.goto(login_url)","sourceCodeStart":1,"sourceCodeEnd":32,"githubUrl":"https://github.com/wpscanteam/wpscan/blob/62c9cef471de90095f6b42245a11d0f8172d19c9/lib/wpscan/browser_authenticator.rb#L1-L32","documentation":"WPScan::Error::BrowserFailed raised at the top of BrowserAuthenticator.authenticate (lib/wpscan/browser_authenticator.rb:13-17) when --expect-saml is used and $stdin is not a TTY. The SAML flow opens a visible browser, waits for you to complete the IdP login, then blocks on a bare 'gets' for you to press enter; without an interactive stdin that handshake is impossible, so it fails fast before launching anything.","triggerScenarios":"Calling WPScan with expect_saml enabled from cron, systemd, CI runners, nohup, a piped shell (wpscan ... < file, curl ... | wpscan), ssh without -t, or a Docker container started without -t; programmatically calling WPScan::BrowserAuthenticator.authenticate(login_url) in a test or daemon where $stdin has been redirected.","commonSituations":"Trying to automate SAML-authenticated scans headlessly; running wpscan inside a pipeline stage; scheduled tasks that were tested interactively first; running under a process supervisor (supervisord, Kubernetes job) where stdin is /dev/null.","solutions":["Run wpscan --expect-saml from a real interactive shell (stdin attached to a terminal)","For containers/ssh allocate a TTY: docker run -it ... or ssh -t","For automation, drop --expect-saml and authenticate another way the target permits (e.g. --http-auth) or capture cookies once interactively and reuse them per your setup","If scripting the gem, gate the call: only enable SAML handling when $stdin.tty? is true"],"exampleFix":"# before (CI job, stdin is not a TTY)\n$ wpscan --url https://example.com --expect-saml\n# => BrowserFailed: SAML authentication needs an interactive terminal ...\n\n# after (interactive shell)\n$ wpscan --url https://example.com --expect-saml\n# browser opens, log in, press enter in the terminal","handlingStrategy":"validation","validationCode":"# Gate the SAML flow on interactivity before invoking the scanner\nunless $stdin.tty?\n  abort '--expect-saml needs an interactive terminal; re-run from a real shell or drop the flag'\nend","typeGuard":null,"tryCatchPattern":"begin\n  WPScan::BrowserAuthenticator.authenticate(login_url)\nrescue WPScan::Error::BrowserFailed => e\n  abort e.message # TTY problem or browser problem: neither is retryable in-process\nend","preventionTips":["Never schedule --expect-saml from cron/CI; SAML by definition needs a human at the IdP","Detect the environment up front: $stdin.tty? and ENV['CI'] checks before building the command","For containers always start with -it when an interactive flow is expected"],"tags":["saml","tty","interactive","wpscan","ruby","stdin"],"backgroundTag":"non-interactive-tty","analyzedSha":"62c9cef471de90095f6b42245a11d0f8172d19c9","analyzedAt":"2026-08-21T17:10:47.902Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}