certgraph 0.2.0__tar.gz → 0.3.0__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.
- {certgraph-0.2.0 → certgraph-0.3.0}/PKG-INFO +1 -1
- {certgraph-0.2.0 → certgraph-0.3.0}/pyproject.toml +1 -1
- {certgraph-0.2.0 → certgraph-0.3.0}/src/certgraph/certgraph.py +16 -14
- {certgraph-0.2.0 → certgraph-0.3.0}/src/certgraph.egg-info/PKG-INFO +1 -1
- certgraph-0.3.0/tests/test_export_dot.py +54 -0
- certgraph-0.2.0/tests/test_export_dot.py +0 -9
- {certgraph-0.2.0 → certgraph-0.3.0}/LICENSE +0 -0
- {certgraph-0.2.0 → certgraph-0.3.0}/README.md +0 -0
- {certgraph-0.2.0 → certgraph-0.3.0}/setup.cfg +0 -0
- {certgraph-0.2.0 → certgraph-0.3.0}/src/certgraph/__init__.py +0 -0
- {certgraph-0.2.0 → certgraph-0.3.0}/src/certgraph.egg-info/SOURCES.txt +0 -0
- {certgraph-0.2.0 → certgraph-0.3.0}/src/certgraph.egg-info/dependency_links.txt +0 -0
- {certgraph-0.2.0 → certgraph-0.3.0}/src/certgraph.egg-info/requires.txt +0 -0
- {certgraph-0.2.0 → certgraph-0.3.0}/src/certgraph.egg-info/top_level.txt +0 -0
- {certgraph-0.2.0 → certgraph-0.3.0}/tests/test_clear.py +0 -0
- {certgraph-0.2.0 → certgraph-0.3.0}/tests/test_fingerprint_search.py +0 -0
- {certgraph-0.2.0 → certgraph-0.3.0}/tests/test_get_certificate.py +0 -0
- {certgraph-0.2.0 → certgraph-0.3.0}/tests/test_get_child_fingerprints.py +0 -0
- {certgraph-0.2.0 → certgraph-0.3.0}/tests/test_get_issuer_fingerprint.py +0 -0
- {certgraph-0.2.0 → certgraph-0.3.0}/tests/test_graph.py +0 -0
- {certgraph-0.2.0 → certgraph-0.3.0}/tests/test_import_certificates.py +0 -0
- {certgraph-0.2.0 → certgraph-0.3.0}/tests/test_remove_certificate.py +0 -0
|
@@ -5,7 +5,7 @@ from rapidfuzz import fuzz
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class certgraph:
|
|
8
|
-
"""Class for creating and exploring directed graphs of
|
|
8
|
+
"""Class for creating and exploring directed graphs of X.509 certificates."""
|
|
9
9
|
|
|
10
10
|
def __init__(self) -> None:
|
|
11
11
|
"""Initialise an empty certgraph object."""
|
|
@@ -88,25 +88,27 @@ class certgraph:
|
|
|
88
88
|
"""
|
|
89
89
|
return [f"{edge[0][:8]} -> {edge[1][:8]}" for edge in self._graph.edges()]
|
|
90
90
|
|
|
91
|
-
def export_dot(self
|
|
92
|
-
"""
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if format not in allowed_types:
|
|
96
|
-
raise ValueError(
|
|
97
|
-
f"Cannot export dot graph of type {format} - must be one of {allowed_types}"
|
|
98
|
-
)
|
|
91
|
+
def export_dot(self) -> str:
|
|
92
|
+
"""
|
|
93
|
+
Export the current certificate digraph in the dot language.
|
|
99
94
|
|
|
95
|
+
Returns:
|
|
96
|
+
The certificate digraph rendered as a dot language string.
|
|
97
|
+
"""
|
|
100
98
|
dot_graph = nx.DiGraph()
|
|
101
99
|
|
|
102
100
|
for fingerprint, data in self._graph.nodes(data=True):
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
cert: x509.Certificate = data["certificate"]
|
|
102
|
+
validity_time_str = f"{cert.not_valid_before_utc.isoformat()} → {cert.not_valid_after_utc.isoformat()}"
|
|
103
|
+
|
|
104
|
+
label = f"DN: {cert.subject.rfc4514_string()}"
|
|
105
|
+
label += f"\nValid: {validity_time_str}"
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
dot_graph.add_node(fingerprint, label=label, shape="box")
|
|
108
|
+
|
|
109
|
+
dot_graph.add_edges_from(self._graph.edges())
|
|
108
110
|
|
|
109
|
-
return
|
|
111
|
+
return nx.drawing.nx_pydot.to_pydot(dot_graph).to_string()
|
|
110
112
|
|
|
111
113
|
def clear(self) -> certgraph:
|
|
112
114
|
"""
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
def test_export_dot_returns_string(graph):
|
|
2
|
+
assert isinstance(graph.export_dot(), str)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_export_dot_empty_graph_has_no_edges(graph):
|
|
6
|
+
dot = graph.export_dot()
|
|
7
|
+
assert "digraph" in dot
|
|
8
|
+
assert "->" not in dot
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_export_dot_includes_node_labels(graph, self_signed_cert):
|
|
12
|
+
graph.import_certificates(self_signed_cert)
|
|
13
|
+
dn = self_signed_cert.subject.rfc4514_string()
|
|
14
|
+
|
|
15
|
+
dot = graph.export_dot()
|
|
16
|
+
|
|
17
|
+
assert f"DN: {dn}" in dot
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_export_dot_includes_validity_period(graph, self_signed_cert):
|
|
21
|
+
graph.import_certificates(self_signed_cert)
|
|
22
|
+
not_before = self_signed_cert.not_valid_before_utc.isoformat()
|
|
23
|
+
not_after = self_signed_cert.not_valid_after_utc.isoformat()
|
|
24
|
+
|
|
25
|
+
dot = graph.export_dot()
|
|
26
|
+
|
|
27
|
+
# joined with an arrow character rather than '->', to avoid being confused with edge syntax
|
|
28
|
+
assert f"Valid: {not_before} → {not_after}" in dot
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_export_dot_node_shape_is_box(graph, self_signed_cert):
|
|
32
|
+
graph.import_certificates(self_signed_cert)
|
|
33
|
+
|
|
34
|
+
dot = graph.export_dot()
|
|
35
|
+
|
|
36
|
+
assert "shape=box" in dot
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_export_dot_includes_edges(graph, chain_certs):
|
|
40
|
+
graph.import_certificates(chain_certs)
|
|
41
|
+
|
|
42
|
+
dot = graph.export_dot()
|
|
43
|
+
|
|
44
|
+
# one self-loop for the root, plus one edge per subsequent certificate
|
|
45
|
+
assert dot.count("->") == len(chain_certs)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_export_dot_does_not_write_file(graph, self_signed_cert, tmp_path, monkeypatch):
|
|
49
|
+
monkeypatch.chdir(tmp_path)
|
|
50
|
+
graph.import_certificates(self_signed_cert)
|
|
51
|
+
|
|
52
|
+
graph.export_dot()
|
|
53
|
+
|
|
54
|
+
assert not (tmp_path / "test_out.dot").exists()
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
|
|
3
|
-
# export_dot()'s actual output generation is still rough and not covered here.
|
|
4
|
-
# Only the argument validation, which happens before anything is written, is tested.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def test_export_dot_rejects_invalid_format(graph):
|
|
8
|
-
with pytest.raises(ValueError):
|
|
9
|
-
graph.export_dot(format="jpeg")
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|