runspec-webops-core 0.3.1__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {runspec_webops_core-0.3.1 → runspec_webops_core-0.3.2}/.gitignore +2 -0
- {runspec_webops_core-0.3.1 → runspec_webops_core-0.3.2}/CHANGELOG.md +9 -0
- {runspec_webops_core-0.3.1 → runspec_webops_core-0.3.2}/PKG-INFO +1 -1
- {runspec_webops_core-0.3.1 → runspec_webops_core-0.3.2}/pyproject.toml +1 -1
- {runspec_webops_core-0.3.1 → runspec_webops_core-0.3.2}/runspec_webops_core/tls.py +10 -4
- {runspec_webops_core-0.3.1 → runspec_webops_core-0.3.2}/tests/test_tls.py +27 -0
- {runspec_webops_core-0.3.1 → runspec_webops_core-0.3.2}/README.md +0 -0
- {runspec_webops_core-0.3.1 → runspec_webops_core-0.3.2}/runspec_webops_core/__init__.py +0 -0
- {runspec_webops_core-0.3.1 → runspec_webops_core-0.3.2}/runspec_webops_core/dns.py +0 -0
- {runspec_webops_core-0.3.1 → runspec_webops_core-0.3.2}/runspec_webops_core/errors.py +0 -0
- {runspec_webops_core-0.3.1 → runspec_webops_core-0.3.2}/runspec_webops_core/http.py +0 -0
- {runspec_webops_core-0.3.1 → runspec_webops_core-0.3.2}/runspec_webops_core/openapi.py +0 -0
- {runspec_webops_core-0.3.1 → runspec_webops_core-0.3.2}/runspec_webops_core/proxy.py +0 -0
- {runspec_webops_core-0.3.1 → runspec_webops_core-0.3.2}/tests/__init__.py +0 -0
- {runspec_webops_core-0.3.1 → runspec_webops_core-0.3.2}/tests/test_dns.py +0 -0
- {runspec_webops_core-0.3.1 → runspec_webops_core-0.3.2}/tests/test_http.py +0 -0
- {runspec_webops_core-0.3.1 → runspec_webops_core-0.3.2}/tests/test_openapi.py +0 -0
- {runspec_webops_core-0.3.1 → runspec_webops_core-0.3.2}/tests/test_proxy.py +0 -0
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# runspec-webops-core Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.2] — 2026-11-12
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- `connect_peer` (behind `cert-check`, `cert-chain` and `tls-info`) failed on
|
|
8
|
+
Python 3.13+ with `'bytes' object has no attribute 'public_bytes'`: the public
|
|
9
|
+
`SSLSocket.get_unverified_chain()` returns DER bytes, not certificate objects.
|
|
10
|
+
Both shapes are read now.
|
|
11
|
+
|
|
3
12
|
## [0.3.1] — 2026-11-05
|
|
4
13
|
|
|
5
14
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: runspec-webops-core
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Pure-Python HTTPS/TLS certificate + web-endpoint helpers — the importable core behind the runspec web runnables (no runspec dependency, no runnables)
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Provides-Extra: dev
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "runspec-webops-core"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.2"
|
|
8
8
|
requires-python = ">=3.10"
|
|
9
9
|
description = "Pure-Python HTTPS/TLS certificate + web-endpoint helpers — the importable core behind the runspec web runnables (no runspec dependency, no runnables)"
|
|
10
10
|
readme = "README.md"
|
|
@@ -201,8 +201,8 @@ def client_ssl_context(insecure: bool = False, ca_bundle: str | None = None) ->
|
|
|
201
201
|
def _peer_chain(ssock: ssl.SSLSocket) -> list[CertInfo]:
|
|
202
202
|
"""Return the server-presented certificate chain (leaf first).
|
|
203
203
|
|
|
204
|
-
Prefers the public ``SSLSocket.get_unverified_chain()`` (Python 3.13
|
|
205
|
-
falls back to the underlying ``_sslobj`` method (3.10–3.12), and finally to
|
|
204
|
+
Prefers the public ``SSLSocket.get_unverified_chain()`` (Python 3.13+,
|
|
205
|
+
which returns DER bytes), falls back to the underlying ``_sslobj`` method (3.10–3.12), and finally to
|
|
206
206
|
the leaf-only certificate via ``getpeercert`` when neither is available.
|
|
207
207
|
"""
|
|
208
208
|
getter = getattr(ssock, "get_unverified_chain", None)
|
|
@@ -218,8 +218,14 @@ def _peer_chain(ssock: ssl.SSLSocket) -> list[CertInfo]:
|
|
|
218
218
|
if raw:
|
|
219
219
|
out: list[CertInfo] = []
|
|
220
220
|
for cert in raw:
|
|
221
|
-
|
|
222
|
-
|
|
221
|
+
if isinstance(cert, (bytes, bytearray)):
|
|
222
|
+
# Python 3.13+ public API: DER-encoded bytes.
|
|
223
|
+
der = bytes(cert)
|
|
224
|
+
out.append(cert_info_from_ssl(_decode_der(der), der))
|
|
225
|
+
else:
|
|
226
|
+
# 3.10–3.12 ``_sslobj`` method: ``_ssl.Certificate`` objects.
|
|
227
|
+
der = ssl.PEM_cert_to_DER_cert(cert.public_bytes())
|
|
228
|
+
out.append(cert_info_from_ssl(cert.get_info(), der))
|
|
223
229
|
return out
|
|
224
230
|
|
|
225
231
|
# Fallback: only the leaf is recoverable on this interpreter.
|
|
@@ -132,3 +132,30 @@ def test_chain_report_marks_root() -> None:
|
|
|
132
132
|
rep = chain_report(peer, now=NOW)
|
|
133
133
|
assert [link["self_signed"] for link in rep] == [False, True]
|
|
134
134
|
assert rep[0]["days_remaining"] == 30
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _a_der_cert() -> bytes:
|
|
138
|
+
"""Any real DER certificate: the first system CA (no network needed)."""
|
|
139
|
+
certs = ssl.create_default_context().get_ca_certs(binary_form=True)
|
|
140
|
+
if not certs:
|
|
141
|
+
pytest.skip("no system CA certificates to use as a sample")
|
|
142
|
+
return certs[0]
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def test_peer_chain_reads_der_bytes_from_public_api() -> None:
|
|
146
|
+
# Python 3.13+ ``SSLSocket.get_unverified_chain()`` returns DER bytes, not
|
|
147
|
+
# ``_ssl.Certificate`` objects; this used to fail with
|
|
148
|
+
# "'bytes' object has no attribute 'public_bytes'".
|
|
149
|
+
from runspec_webops_core.tls import _peer_chain
|
|
150
|
+
|
|
151
|
+
der = _a_der_cert()
|
|
152
|
+
|
|
153
|
+
class FakeSocket:
|
|
154
|
+
def get_unverified_chain(self) -> list[bytes]:
|
|
155
|
+
return [der, der]
|
|
156
|
+
|
|
157
|
+
chain = _peer_chain(FakeSocket()) # type: ignore[arg-type]
|
|
158
|
+
assert len(chain) == 2
|
|
159
|
+
assert chain[0].subject
|
|
160
|
+
assert chain[0].valid_to
|
|
161
|
+
assert chain[0].fingerprint_sha256
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|