2 Commits

Author SHA1 Message Date
klemek 225b13a7e3 chore: release 1.5.1
Python Lint CI / ruff (push) Successful in 2m1s
Docker CI / docker-build (push) Successful in 3m13s
Python Lint CI / ruff-format-check (push) Successful in 1m39s
Python Lint CI / ty (push) Successful in 1m57s
Python Test CI / coverage (push) Successful in 1m38s
2026-06-03 19:51:21 +02:00
klemek c186668208 fix: check loopback before cert creation 2026-06-03 19:51:08 +02:00
4 changed files with 36 additions and 4 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "stapler" name = "stapler"
version = "1.5.0" version = "1.5.1"
description = "Static pages as simple as a gzip file" description = "Static pages as simple as a gzip file"
requires-python = ">=3.14" requires-python = ">=3.14"
dependencies = [ dependencies = [
+17 -1
View File
@@ -5,6 +5,8 @@ import ssl
import subprocess import subprocess
import typing import typing
import requests
from stapler.strings import valid_host from stapler.strings import valid_host
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
@@ -49,6 +51,18 @@ class CertManager:
def exists(self, host: str) -> bool: def exists(self, host: str) -> bool:
return self.__exists_certbot(host) or self.__exists_self_signed(host) return self.__exists_certbot(host) or self.__exists_self_signed(host)
def valid_host(self, host: str) -> bool:
try:
response = requests.head(
url=f"http://{host}/.well-known/stapler",
allow_redirects=True,
timeout=5,
stream=False,
)
return type(response.status_code) is int and response.status_code < 400
except Exception:
return False
def init_cert(self, host: str) -> bool: def init_cert(self, host: str) -> bool:
if not self.exists(host): if not self.exists(host):
return self.__create_self_signed(host) return self.__create_self_signed(host)
@@ -196,7 +210,9 @@ class CertManager:
if host is None or not valid_host(host): if host is None or not valid_host(host):
return None return None
self.logger.debug("servername callback: %s", host) self.logger.debug("servername callback: %s", host)
if not self.exists(host) and not self.create_or_update(host): if not self.exists(host) and (
not self.valid_host(host) or not self.create_or_update(host)
):
return None return None
cert_file = self.get_cert(host) cert_file = self.get_cert(host)
key_file = self.get_key(host) key_file = self.get_key(host)
+17 -1
View File
@@ -4,6 +4,8 @@ import subprocess
import typing import typing
import unittest.mock import unittest.mock
import requests
from stapler.cert_manager import CertManager, CertManagerError from stapler.cert_manager import CertManager, CertManagerError
from stapler.params import Parameters from stapler.params import Parameters
@@ -170,10 +172,24 @@ class TestRegistry(BaseTestCase):
self.socket_mock, None, self.context_mock self.socket_mock, None, self.context_mock
) )
def test_servername_callback_fail(self) -> None: def test_servername_callback_fail_no_valid_host(self) -> None:
self._make_self_signed("example.com") self._make_self_signed("example.com")
with (
self.patch("requests.head") as request_mock,
self.patch("ssl.create_default_context", count=0),
):
request_mock.side_effect = Exception()
self.cert_manager.servername_callback(
self.socket_mock, "example.fr", self.context_mock
)
def test_servername_callback_fail_no_binaries(self) -> None:
self._make_self_signed("example.com")
response = requests.Response()
response.status_code = 200
with ( with (
self.patch("shutil.which", count=3), self.patch("shutil.which", count=3),
self.patch("requests.head", response),
self.patch("ssl.create_default_context", count=0), self.patch("ssl.create_default_context", count=0),
): ):
self.cert_manager.servername_callback( self.cert_manager.servername_callback(
Generated
+1 -1
View File
@@ -212,7 +212,7 @@ wheels = [
[[package]] [[package]]
name = "stapler" name = "stapler"
version = "1.5.0" version = "1.5.1"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "requests" }, { name = "requests" },