{"id":"ssl-tls-certificate-verification","version":"1.0.0","primitive":"code_execution","description":"Verify SSL/TLS certificates, check expiry, validate chain, inspect DNS before deployment","registry_refs":["requests","httpx"],"tags":["ssl","tls","certificate","dns","deployment","security","https","verification"],"solves":["expired cert not caught before deploy","verify=False left in production","self-signed cert in prod","cert chain incomplete","DNS mismatch","no expiry buffer check"],"auth_required":false,"verified":true,"last_verified":"2026-04-14","next_check":"2026-07-14","eval_result":"pass","eval_env":"python3.12/linux","mast":["FM-1.1","FM-1.5","FM-2.2","FM-2.4","FM-3.2","FM-3.3"],"ref":"https://arxiv.org/abs/2503.13657","inputs":[],"executable":"# ============================================\n# checklist:     ssl-tls-certificate-verification\n# version:       1.0.0\n# primitive:     code_execution\n# description:   Verify SSL/TLS certificates, check expiry, validate chain, inspect DNS before deployment\n# registry_refs: requests, httpx\n# auth_required: false\n# verified:      true\n# last_verified: 2026-04-14\n# next_check:    2026-07-14\n# eval_result:   pass\n# eval_env:      python3.12/linux\n#\n# tags:   ssl, tls, certificate, dns, deployment, security, https, verification\n# solves: expired cert not caught before deploy, verify=False left in production, self-signed cert in prod, cert chain incomplete, DNS mismatch, no expiry buffer check\n# mast: FM-1.1, FM-1.5, FM-2.2, FM-2.4, FM-3.2, FM-3.3\n# ref:  https://arxiv.org/abs/2503.13657\n#\n# INPUTS:\n#   TEST_HOST          — string, hostname to verify SSL certificate for (default: \"checklist.day\")\n#   EXPIRY_BUFFER_DAYS — int, minimum days before expiry required to pass (default: 30)\n#\n# OUTPUTS:\n#   cert_not_expired      — bool, certificate expiry date is in the future\n#   expiry_buffer_ok      — bool, days remaining exceeds EXPIRY_BUFFER_DAYS\n#   hostname_match        — bool, cert CN/SAN matches TEST_HOST\n#   trusted_ca            — bool, certificate is not self-signed\n#   https_request_ok      — bool, HTTPS GET returned 200\n#   tls_protocol_verified — bool, TLS 1.2 or higher in use\n#   days_remaining        — int, days until certificate expiry\n#   protocol              — str, TLS protocol version negotiated\n# ============================================\n\nimport ssl\nimport sys\nimport socket\nimport datetime\nimport subprocess\nimport requests as _requests\n\n# ----------------------------------------\n# PRE_EXECUTION\n# FM-2.2: fetch ground truth for all registry_refs\n# ----------------------------------------\n\nREGISTRY_REFS = [\"requests\", \"httpx\"]\nMAX_RETRIES = 2\nregistries = {}\n\nfor lib in REGISTRY_REFS:\n    for attempt in range(MAX_RETRIES):\n        try:\n            response = _requests.get(\n                f\"https://checklist.day/api/registry/{lib}\",\n                timeout=10\n            )\n            if response.status_code == 200:\n                registries[lib] = response.json()\n                break\n        except _requests.exceptions.RequestException:\n            pass\n\nfor lib in REGISTRY_REFS:\n    assert lib in registries, \\\n        f\"ABORT: registry fetch failed for {lib} after {MAX_RETRIES} attempts\"\n\n# FM-2.4: surface breaking warnings\nfor lib, registry in registries.items():\n    breaking = [\n        w for w in registry.get(\"warnings\", [])\n        if w.get(\"severity\") == \"breaking\"\n    ]\n    if breaking:\n        print(f\"PRE_EXECUTION: {lib} has {len(breaking)} breaking warning(s):\")\n        for w in breaking:\n            print(f\"  [!] [{w.get('affected_versions', 'all')}] {w['message'][:120]}\")\n            print(f\"      fix: {w['fix'][:100]}\")\n\nprint()\nprint(\"PRE_EXECUTION: all registry refs verified ✓\")\n\n# ----------------------------------------\n# KNOWN FAILURE MODES\n#\n# 1. verify=False left in production — disables all cert validation silently.\n#    Never use requests.get(url, verify=False) in production code.\n#    Common pattern: dev bypasses SSL for testing, forgets to revert.\n#\n# 2. No expiry buffer — cert valid today, expires in 3 days, deploy breaks on day 4.\n#    Always check expiry with a minimum buffer (30 days recommended).\n#\n# 3. Self-signed cert in prod — works locally, fails for every external client.\n#    Check issuer — must be a trusted CA, not the domain itself.\n#\n# 4. Incomplete cert chain — leaf cert valid but intermediate not served.\n#    Causes failures in strict clients even if browser shows padlock.\n#\n# 5. DNS mismatch — cert issued for www.example.com, deployed at example.com.\n#    CN/SAN must match the hostname being verified.\n#\n# 6. Wrong port — SSL check on port 443 passes, but service runs on 8443.\n#    Always verify against the actual port in use.\n# ----------------------------------------\n\nEXPIRY_BUFFER_DAYS = 30   # warn if cert expires within this window\nDEFAULT_TIMEOUT = 10\nDEFAULT_PORT = 443\n\n\ndef get_cert_info(hostname: str, port: int = DEFAULT_PORT) -> dict:\n    \"\"\"\n    Fetch SSL certificate info using stdlib ssl — no extra dependencies.\n    FM-1.5: explicit timeout — never hang on unresponsive host.\n    \"\"\"\n    context = ssl.create_default_context()\n\n    with socket.create_connection((hostname, port), timeout=DEFAULT_TIMEOUT) as sock:\n        with context.wrap_socket(sock, server_hostname=hostname) as ssock:\n            cert = ssock.getpeercert()\n            # get full chain depth\n            chain = ssock.getpeercert(binary_form=False)\n            return {\n                \"cert\": cert,\n                \"protocol\": ssock.version(),\n                \"cipher\": ssock.cipher(),\n            }\n\n\ndef check_expiry(cert: dict, buffer_days: int = EXPIRY_BUFFER_DAYS) -> dict:\n    \"\"\"\n    FM-3.2: check expiry before asserting cert is valid.\n    Returns days_remaining and whether within warning buffer.\n    \"\"\"\n    not_after = cert.get(\"notAfter\")\n    assert not_after, \"ABORT: cert missing notAfter field\"\n\n    expiry_dt = datetime.datetime.strptime(not_after, \"%b %d %H:%M:%S %Y %Z\")\n    expiry_dt = expiry_dt.replace(tzinfo=datetime.timezone.utc)\n    now = datetime.datetime.now(datetime.timezone.utc)\n    days_remaining = (expiry_dt - now).days\n\n    return {\n        \"expiry_date\": expiry_dt.strftime(\"%Y-%m-%d\"),\n        \"days_remaining\": days_remaining,\n        \"within_buffer\": days_remaining <= buffer_days,\n        \"expired\": days_remaining < 0,\n    }\n\n\ndef check_hostname_match(cert: dict, hostname: str) -> bool:\n    \"\"\"\n    FM-3.3: verify cert CN/SAN matches the target hostname.\n    SAN (Subject Alternative Names) takes precedence over CN.\n    \"\"\"\n    # Check SANs first\n    san_list = []\n    for san_type, san_value in cert.get(\"subjectAltName\", []):\n        if san_type == \"DNS\":\n            san_list.append(san_value.lower())\n\n    if san_list:\n        hostname_lower = hostname.lower()\n        for san in san_list:\n            if san == hostname_lower:\n                return True\n            # wildcard match: *.example.com matches sub.example.com\n            if san.startswith(\"*.\"):\n                wildcard_domain = san[2:]\n                if hostname_lower.endswith(\".\" + wildcard_domain):\n                    return True\n        return False\n\n    # Fall back to CN\n    for rdn in cert.get(\"subject\", []):\n        for key, value in rdn:\n            if key == \"commonName\":\n                return value.lower() == hostname.lower()\n\n    return False\n\n\ndef check_issuer(cert: dict) -> dict:\n    \"\"\"\n    FM-2.4: surface self-signed cert warning — issuer == subject is a red flag.\n    \"\"\"\n    subject = dict(x[0] for x in cert.get(\"subject\", []))\n    issuer = dict(x[0] for x in cert.get(\"issuer\", []))\n\n    is_self_signed = subject.get(\"commonName\") == issuer.get(\"commonName\")\n\n    return {\n        \"issuer_org\": issuer.get(\"organizationName\", \"unknown\"),\n        \"issuer_cn\": issuer.get(\"commonName\", \"unknown\"),\n        \"is_self_signed\": is_self_signed,\n    }\n\n\ndef verify_https_request(url: str) -> int:\n    \"\"\"\n    FM-2.6: always use verify=True (default) — never disable SSL verification.\n    This will raise SSLError if cert is invalid.\n    \"\"\"\n    import requests\n    response = requests.get(url, timeout=DEFAULT_TIMEOUT, verify=True)\n    return response.status_code\n\n\n# ----------------------------------------\n# EXECUTION\n# Test against a known-good public host: checklist.day\n# FM-1.1: read-only SSL checks — idempotent, no side effects\n# ----------------------------------------\n\ntry:\n    import requests\nexcept ImportError:\n    pkg = registries[\"requests\"][\"install\"][0][\"cmd\"].replace(\"pip install \", \"\").strip()\n    print(f\"\\nEXECUTION: requests not found — installing {pkg}...\")\n    subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", pkg])\n    import requests\n\nTEST_HOST = \"checklist.day\"\nTEST_URL = f\"https://{TEST_HOST}\"\n\nprint()\nprint(f\"EXECUTION: checking SSL certificate for {TEST_HOST}...\")\n\ncert_data = get_cert_info(TEST_HOST)\ncert = cert_data[\"cert\"]\n\nexpiry = check_expiry(cert)\nhostname_match = check_hostname_match(cert, TEST_HOST)\nissuer = check_issuer(cert)\nstatus_code = verify_https_request(TEST_URL)\n\nprint(f\"  protocol      : {cert_data['protocol']}\")\nprint(f\"  cipher        : {cert_data['cipher'][0]}\")\nprint(f\"  expires       : {expiry['expiry_date']}  ({expiry['days_remaining']} days remaining)\")\nprint(f\"  hostname match: {hostname_match}\")\nprint(f\"  issuer        : {issuer['issuer_org']} ({issuer['issuer_cn']})\")\nprint(f\"  self-signed   : {issuer['is_self_signed']}\")\nprint(f\"  https status  : {status_code}\")\n\n# FM-2.4: surface warnings — do not withhold\nif expiry[\"within_buffer\"] and not expiry[\"expired\"]:\n    print(f\"\\n  [!] WARNING: cert expires in {expiry['days_remaining']} days — renew before deployment\")\nif expiry[\"expired\"]:\n    print(f\"\\n  [!] CRITICAL: cert is EXPIRED — do not deploy\")\nif issuer[\"is_self_signed\"]:\n    print(f\"\\n  [!] WARNING: self-signed certificate — not trusted by external clients\")\n\n# ----------------------------------------\n# POST_EXECUTION\n# FM-3.2: verify all checks before asserting PASS\n# FM-3.3: exact assertions on each check\n# ----------------------------------------\n\nassert not expiry[\"expired\"], \\\n    f\"FAIL: certificate is expired as of {expiry['expiry_date']}\"\n\nassert expiry[\"days_remaining\"] > EXPIRY_BUFFER_DAYS, \\\n    f\"FAIL: cert expires in {expiry['days_remaining']} days — below {EXPIRY_BUFFER_DAYS}-day buffer\"\n\nassert hostname_match, \\\n    f\"FAIL: hostname '{TEST_HOST}' does not match cert CN/SAN\"\n\nassert not issuer[\"is_self_signed\"], \\\n    f\"FAIL: self-signed certificate detected — not valid for production\"\n\nassert status_code == 200, \\\n    f\"FAIL: HTTPS request returned {status_code}, expected 200\"\n\n# Verify protocol is TLS 1.2 or higher\nassert cert_data[\"protocol\"] in (\"TLSv1.2\", \"TLSv1.3\"), \\\n    f\"FAIL: insecure protocol '{cert_data['protocol']}' — require TLS 1.2+\"\n\nprint()\nprint(\"POST_EXECUTION: cert not expired ✓\")\nprint(f\"POST_EXECUTION: expiry buffer ok ✓  ({expiry['days_remaining']} days > {EXPIRY_BUFFER_DAYS}-day minimum)\")\nprint(\"POST_EXECUTION: hostname match ✓\")\nprint(\"POST_EXECUTION: trusted CA issuer ✓\")\nprint(\"POST_EXECUTION: HTTPS request succeeded ✓\")\nprint(f\"POST_EXECUTION: protocol verified ✓  ({cert_data['protocol']})\")\n\nresult = {\n    \"status\": \"pass\",\n    \"cert_not_expired\": True,\n    \"expiry_buffer_ok\": True,\n    \"hostname_match\": True,\n    \"trusted_ca\": True,\n    \"https_request_ok\": True,\n    \"tls_protocol_verified\": True,\n    \"days_remaining\": expiry[\"days_remaining\"],\n    \"protocol\": cert_data[\"protocol\"],\n}\nprint(result)\nprint(\"PASS\")\n"}