Skip to content

Commit f8bb576

Browse files
authored
Merge pull request #39 from languitar/feature/graceful-failures
fix: don't leak Python exceptions to the user
2 parents 337f31f + 4415d7e commit f8bb576

File tree

2 files changed

+62
-6
lines changed

2 files changed

+62
-6
lines changed

passgithelper.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,13 @@ def main(argv: Optional[Sequence[str]] = None) -> None:
448448
sys.exit(1)
449449

450450
if action == "get":
451-
get_password(request, mapping)
451+
try:
452+
get_password(request, mapping)
453+
except Exception as error:
454+
print( # noqa: T001
455+
"Unable to retrieve entry: {error}".format(error=error), file=sys.stderr
456+
)
457+
sys.exit(1)
452458
else:
453459
LOGGER.info("Action %s is currently not supported", action)
454460
sys.exit(1)

test_passgithelper.py

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import configparser
22
from dataclasses import dataclass
33
import io
4+
from subprocess import CalledProcessError
45
from typing import Any, Iterable, Optional, Sequence, Text
56

67
import pytest
@@ -13,7 +14,7 @@
1314
class HelperConfig:
1415
xdg_dir: Optional[str]
1516
request: str
16-
entry_data: bytes
17+
entry_data: Optional[bytes]
1718
entry_name: Optional[str] = None
1819

1920

@@ -27,7 +28,10 @@ def _helper_config(mocker: MockFixture, request: Any) -> Iterable[None]:
2728
)
2829

2930
subprocess_mock = mocker.patch("subprocess.check_output")
30-
subprocess_mock.return_value = request.param.entry_data
31+
if request.param.entry_data:
32+
subprocess_mock.return_value = request.param.entry_data
33+
else:
34+
subprocess_mock.side_effect = CalledProcessError(1, ["pass"], "pass failed")
3135

3236
yield
3337

@@ -216,10 +220,13 @@ def test_smoke_resolve(self, capsys: Any) -> None:
216220
indirect=True,
217221
)
218222
@pytest.mark.usefixtures("_helper_config")
219-
def test_path_used_if_present_fails(self) -> None:
220-
with pytest.raises(ValueError, match="No mapping section"):
223+
def test_path_used_if_present_fails(self, capsys: Any) -> None:
224+
with pytest.raises(SystemExit):
221225
passgithelper.main(["get"])
222226

227+
_, err = capsys.readouterr()
228+
assert "No mapping section" in err
229+
223230
@pytest.mark.parametrize(
224231
"_helper_config",
225232
[
@@ -364,7 +371,7 @@ def test_prefix_skipping(self, capsys: Any) -> None:
364371
)
365372
@pytest.mark.usefixtures("_helper_config")
366373
def test_select_unknown_extractor(self) -> None:
367-
with pytest.raises(KeyError):
374+
with pytest.raises(SystemExit):
368375
passgithelper.main(["get"])
369376

370377
@pytest.mark.parametrize(
@@ -450,3 +457,46 @@ def test_uses_utf8_by_default(self, capsys: Any) -> None:
450457

451458
out, _ = capsys.readouterr()
452459
assert out == "password=täßt\n"
460+
461+
@pytest.mark.parametrize(
462+
"_helper_config",
463+
[
464+
HelperConfig(
465+
"test_data/smoke",
466+
"""
467+
protocol=https
468+
host=mytest.com""",
469+
None,
470+
"dev/mytest",
471+
),
472+
],
473+
indirect=True,
474+
)
475+
@pytest.mark.usefixtures("_helper_config")
476+
def test_fails_gracefully_on_pass_errors(self, capsys: Any) -> None:
477+
with pytest.raises(SystemExit):
478+
passgithelper.main(["get"])
479+
480+
_, err = capsys.readouterr()
481+
assert "Unable to retrieve" in err
482+
483+
@pytest.mark.parametrize(
484+
"_helper_config",
485+
[
486+
HelperConfig(
487+
"test_data/smoke",
488+
"""
489+
protocol=https
490+
host=unknown""",
491+
"ignored".encode("UTF-8"),
492+
),
493+
],
494+
indirect=True,
495+
)
496+
@pytest.mark.usefixtures("_helper_config")
497+
def test_fails_gracefully_on_missing_entries(self, capsys: Any) -> None:
498+
with pytest.raises(SystemExit):
499+
passgithelper.main(["get"])
500+
501+
_, err = capsys.readouterr()
502+
assert "Unable to retrieve" in err

0 commit comments

Comments
 (0)