require 'test_helper'
require 'mechanize'

class SecurityTest < Minitest::Test
  include WebserverHelper

  def test_certificate_level
    level = "intermediate"
    output = `#{analyze_cmd(domain, level)}`

    assert_match %r|has intermediate ssl/tls\nand complies with the '#{level}' level|, output, "Expected to comply with #{level} level :\n#{output.inspect}"
    refute_match %r|consider enabling OCSP Stapling|, output, 'Expected to have OCSP stapling enabled'
  end

  def test_certificate
    output = `#{check_ssl_cert_cmd(domain)}`

    assert_match /\ASSL_CERT OK/, output, output
  end

  def test_accepts_tls_v1
    output = `#{openssl_verify_cmd(domain, "-tls1")}`

    assert_match /Verify return code: 0 \(ok\)/, output, "Expected to accept TLSv1"
  end

  def test_refuse_ssl_v3
    output = `#{openssl_verify_cmd(domain, "-ssl3")}`

    assert_match /sslv3 alert handshake failure/, output, "Expected to refuse SSLv3"
  end

  def test_hsts_header
    agent = Mechanize.new { |a|
      a.follow_redirect = false
    }
    page = agent.get("https://#{domain}")

    assert_has_header("Strict-Transport-Security", page)
  end

  def check_ssl_cert_cmd(domain)
    # check_ssl_cert is a Nagios plugin, usable outside of Nagios
    # cf. https://trac.id.ethz.ch/projects/nagios_plugins/wiki/check_ssl_cert

    args = [
      "--rootcert", root_certificate,
      "--openssl", openssl_path(:system),
      "--issuer", %Q("Gandi Standard SSL CA 2"),
      "--warning", 60,
      "--critical", 30,
      "--cn", %Q("*.example.com"),
      "--host-cn",
      "--ocsp",
      "--host", domain,
    ].join(" ")

    "vendor/check_ssl_cert/check_ssl_cert #{args}"
  end

  def analyze_cmd(domain, level = "intermediate")
    # Cipherscan helps audit SSL configuration
    # cf. https://github.com/jvehent/cipherscan

    args = [
      "-o", openssl_path(:local),
      "-l", level,
      "-t", domain
    ].join(' ')

    "vendor/cipherscan/analyze.py #{args}"
  end

  def openssl_verify_cmd(domain, options = "")
    args = [
      "-CAfile", "#{root_certificate}",
      "-connect", "#{domain}:443",
      options,
      "2>&1",
    ].join(" ")

    "echo QUIT | #{openssl_path} s_client #{args}"
  end

  def openssl_path(variant = :system)
    case variant
    when :local
      "vendor/cipherscan/openssl-darwin64"
    else
      `which openssl`.chop
    end
  end

  def root_certificate
    "test/certs/AddTrust_External_CA_Root.pem"
  end

end
