deploy-checkup 0.1.0__tar.gz → 0.1.1__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.
Files changed (39) hide show
  1. {deploy_checkup-0.1.0/src/deploy_checkup.egg-info → deploy_checkup-0.1.1}/PKG-INFO +1 -1
  2. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/pyproject.toml +1 -1
  3. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/src/deploy_checkup/checks/cors.py +3 -4
  4. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/src/deploy_checkup/checks/exposed_files.py +3 -4
  5. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/src/deploy_checkup/checks/headers.py +3 -4
  6. deploy_checkup-0.1.1/src/deploy_checkup/models.py +31 -0
  7. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1/src/deploy_checkup.egg-info}/PKG-INFO +1 -1
  8. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/tests/test_cors.py +16 -1
  9. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/tests/test_exposed_files.py +13 -1
  10. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/tests/test_headers.py +4 -2
  11. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/tests/test_models.py +8 -1
  12. deploy_checkup-0.1.0/src/deploy_checkup/models.py +0 -19
  13. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/LICENSE +0 -0
  14. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/README.md +0 -0
  15. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/setup.cfg +0 -0
  16. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/src/deploy_checkup/__init__.py +0 -0
  17. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/src/deploy_checkup/__main__.py +0 -0
  18. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/src/deploy_checkup/checks/__init__.py +0 -0
  19. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/src/deploy_checkup/checks/cold_start.py +0 -0
  20. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/src/deploy_checkup/checks/health.py +0 -0
  21. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/src/deploy_checkup/checks/https.py +0 -0
  22. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/src/deploy_checkup/checks/reachable.py +0 -0
  23. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/src/deploy_checkup/cli.py +0 -0
  24. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/src/deploy_checkup/hosts.py +0 -0
  25. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/src/deploy_checkup/report.py +0 -0
  26. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/src/deploy_checkup/runner.py +0 -0
  27. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/src/deploy_checkup.egg-info/SOURCES.txt +0 -0
  28. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/src/deploy_checkup.egg-info/dependency_links.txt +0 -0
  29. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/src/deploy_checkup.egg-info/entry_points.txt +0 -0
  30. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/src/deploy_checkup.egg-info/requires.txt +0 -0
  31. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/src/deploy_checkup.egg-info/top_level.txt +0 -0
  32. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/tests/test_cli.py +0 -0
  33. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/tests/test_cold_start.py +0 -0
  34. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/tests/test_health.py +0 -0
  35. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/tests/test_hosts.py +0 -0
  36. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/tests/test_https.py +0 -0
  37. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/tests/test_reachable.py +0 -0
  38. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/tests/test_report.py +0 -0
  39. {deploy_checkup-0.1.0 → deploy_checkup-0.1.1}/tests/test_runner.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deploy-checkup
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Check whether your deployed app is set up safely, in plain language, with fixes for your host.
5
5
  Author: Nandhagopal
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "deploy-checkup"
7
- version = "0.1.0"
7
+ version = "0.1.1"
8
8
  description = "Check whether your deployed app is set up safely, in plain language, with fixes for your host."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  import httpx
2
2
 
3
- from deploy_checkup.models import Finding, Level
3
+ from deploy_checkup.models import Finding, Level, could_not_finish
4
4
 
5
5
  CHECK = "cors"
6
6
  MADE_UP_SITE = "https://deploy-checkup-test.invalid" # .invalid can never be a real website
@@ -12,9 +12,8 @@ def check_cors(client: httpx.Client, api_url: str, frontend_url: str) -> list[Fi
12
12
  try:
13
13
  as_frontend = client.get(api_url, headers={"Origin": frontend})
14
14
  as_stranger = client.get(api_url, headers={"Origin": MADE_UP_SITE})
15
- except httpx.RequestError:
16
- return [] # the other checks already report connection problems
17
-
15
+ except httpx.RequestError as error:
16
+ return [could_not_finish(CHECK, error)]
18
17
  return [
19
18
  _check_own_frontend(as_frontend, frontend),
20
19
  _check_other_sites(as_stranger),
@@ -2,8 +2,7 @@ import re
2
2
 
3
3
  import httpx
4
4
 
5
- from deploy_checkup.models import Finding, Level
6
-
5
+ from deploy_checkup.models import Finding, Level, could_not_finish
7
6
  CHECK = "exposed_files"
8
7
 
9
8
 
@@ -28,8 +27,8 @@ def check_exposed_files(client: httpx.Client, url: str) -> list[Finding]:
28
27
  for path, looks_real in PATHS.items():
29
28
  try:
30
29
  response = client.get(httpx.URL(url).join(path))
31
- except httpx.RequestError:
32
- return [] # the other checks already report connection problems
30
+ except httpx.RequestError as error:
31
+ return findings + [could_not_finish(CHECK, error)]
33
32
 
34
33
  is_html = "text/html" in response.headers.get("content-type", "")
35
34
  if response.status_code == 200 and not is_html and looks_real(response.text):
@@ -1,6 +1,5 @@
1
1
  import httpx
2
- from deploy_checkup.models import Finding, Level
3
-
2
+ from deploy_checkup.models import Finding, Level, could_not_finish
4
3
 
5
4
  CHECK = "headers"
6
5
  SIX_MONTHS= 15_552_000 #seconds
@@ -10,8 +9,8 @@ def check_headers(client: httpx.Client , url:str)->list[Finding]:
10
9
  try:
11
10
  response = client.get(url, follow_redirects=True)
12
11
 
13
- except httpx.RequestError:
14
- return []
12
+ except httpx.RequestError as error:
13
+ return [could_not_finish(CHECK, error)]
15
14
 
16
15
  headers = response.headers
17
16
  return [
@@ -0,0 +1,31 @@
1
+ from dataclasses import dataclass
2
+ from enum import Enum
3
+
4
+
5
+ class Level(str, Enum):
6
+ PASS = "pass"
7
+ WARNING = "warning"
8
+ FAIL = "fail"
9
+ INFO = "info"
10
+
11
+
12
+ @dataclass(frozen=True)
13
+ class Finding:
14
+ check: str
15
+ level: Level
16
+ title: str
17
+ why: str = ""
18
+ fix: str = "" # works on any host
19
+ render_fix: str = "" # replaces fix when the site is on Render
20
+
21
+
22
+
23
+ def could_not_finish(check: str, error: Exception) -> Finding:
24
+ """A check that hit a network error says so, instead of quietly disappearing."""
25
+ return Finding(
26
+ check=check,
27
+ level=Level.INFO,
28
+ title=f"Couldn't finish the {check} check ({type(error).__name__})",
29
+ why="The site stopped answering partway through, so this part wasn't checked.",
30
+ fix="Run deploy-checkup again. If it keeps happening, check your site's logs.",
31
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deploy-checkup
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Check whether your deployed app is set up safely, in plain language, with fixes for your host.
5
5
  Author: Nandhagopal
6
6
  License-Expression: MIT
@@ -77,4 +77,19 @@ def test_sends_frontend_origin_then_made_up_origin():
77
77
 
78
78
  def test_origin_drops_path_and_keeps_unusual_port():
79
79
  assert _origin("https://app.example.com/dashboard?x=1") == "https://app.example.com"
80
- assert _origin("http://127.0.0.1:8000/") == "http://127.0.0.1:8000"
80
+ assert _origin("http://127.0.0.1:8000/") == "http://127.0.0.1:8000"
81
+
82
+ def test_network_error_partway_is_reported_not_hidden():
83
+ calls = []
84
+
85
+ def handler(request):
86
+ calls.append(request)
87
+ if len(calls) == 2:
88
+ raise httpx.ReadTimeout("timed out", request=request)
89
+ return httpx.Response(200)
90
+
91
+ findings = check(handler)
92
+
93
+ assert len(findings) == 1
94
+ assert findings[0].level == Level.INFO
95
+ assert "ReadTimeout" in findings[0].title
@@ -57,4 +57,16 @@ def test_secret_values_are_never_shown():
57
57
  findings = check(site({"/.env": "SECRET_KEY=abc123\n"}))
58
58
 
59
59
  for finding in findings:
60
- assert "abc123" not in finding.title + finding.why + finding.fix
60
+ assert "abc123" not in finding.title + finding.why + finding.fix
61
+
62
+ def test_network_error_keeps_earlier_results_and_says_so():
63
+ def handler(request):
64
+ if request.url.path == "/.git/config":
65
+ raise httpx.ReadTimeout("timed out", request=request)
66
+ return httpx.Response(404)
67
+
68
+ findings = check(handler)
69
+
70
+ assert find(findings, "/.env").level == Level.PASS
71
+ assert findings[-1].level == Level.INFO
72
+ assert "Couldn't finish" in findings[-1].title
@@ -52,13 +52,15 @@ def test_nosniff_missing_warns():
52
52
  assert find(findings, "X-Content-Type-Options").level == Level.WARNING
53
53
 
54
54
 
55
- def test_connection_error_returns_nothing():
55
+ def test_connection_error_is_reported_not_hidden():
56
56
  def handler(request):
57
57
  raise httpx.ConnectError("connection refused", request=request)
58
58
 
59
59
  findings = check_headers(httpx.Client(transport=httpx.MockTransport(handler)), URL)
60
60
 
61
- assert findings == []
61
+ assert len(findings) == 1
62
+ assert findings[0].level == Level.INFO
63
+ assert "Couldn't finish the headers check" in findings[0].title
62
64
 
63
65
  def test_csp_present_passes():
64
66
  findings = run_with_headers({"Content-Security-Policy": "default-src 'self'"})
@@ -36,4 +36,11 @@ def test_level_from_string():
36
36
  assert Level("warning") is Level.WARNING
37
37
 
38
38
  with pytest.raises(ValueError):
39
- Level("warn")
39
+ Level("warn")
40
+ def test_could_not_finish_names_the_check_and_error():
41
+ from deploy_checkup.models import could_not_finish
42
+
43
+ finding = could_not_finish("cors", TimeoutError("slow"))
44
+
45
+ assert finding.level == Level.INFO
46
+ assert finding.title == "Couldn't finish the cors check (TimeoutError)"
@@ -1,19 +0,0 @@
1
- from dataclasses import dataclass
2
- from enum import Enum
3
-
4
-
5
- class Level(str, Enum):
6
- PASS = "pass"
7
- WARNING = "warning"
8
- FAIL = "fail"
9
- INFO = "info"
10
-
11
-
12
- @dataclass(frozen=True)
13
- class Finding:
14
- check: str
15
- level: Level
16
- title: str
17
- why: str = ""
18
- fix: str = "" # works on any host
19
- render_fix: str = "" # replaces fix when the site is on Render
File without changes
File without changes
File without changes