Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.

Commit 55cbc00

Browse files
bennyzgithub-actions[bot]
authored andcommitted
flashers: fail early on header parsing validation
Signed-off-by: Benny Zlotnik <bzlotnik@redhat.com> (cherry picked from commit f381274)
1 parent 2e9031d commit 55cbc00

2 files changed

Lines changed: 29 additions & 8 deletions

File tree

packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def bootloader_shell(self):
7070
pass
7171
yield self.serial
7272

73-
def flash(
73+
def flash( # noqa: C901
7474
self,
7575
path: PathBuf,
7676
*,
@@ -87,6 +87,9 @@ def flash(
8787
if bearer_token:
8888
bearer_token = self._validate_bearer_token(bearer_token)
8989

90+
if headers:
91+
headers = self._validate_header_dict(headers)
92+
9093
"""Flash image to DUT"""
9194
should_download_to_httpd = True
9295
image_url = ""
@@ -689,15 +692,15 @@ def _validate_header_dict(self, header_map: dict[str, str]) -> dict[str, str]:
689692
key = key.strip()
690693
value = value.strip()
691694
if not key:
692-
raise click.ClickException(f"Invalid header key: '{key}'")
695+
raise ArgumentError(f"Invalid header key: '{key}'")
693696

694697
if not token_re.match(key):
695-
raise click.ClickException(f"Invalid header name '{key}': must be an HTTP token (RFC7230)")
698+
raise ArgumentError(f"Invalid header name '{key}': must be an HTTP token (RFC7230)")
696699
if any(c in ("\r", "\n") for c in key) or any(c in ("\r", "\n") for c in value):
697-
raise click.ClickException("Header names/values must not contain CR/LF")
700+
raise ArgumentError("Header names/values must not contain CR/LF")
698701
kl = key.lower()
699702
if kl in seen:
700-
raise click.ClickException(f"Duplicate header '{key}'")
703+
raise ArgumentError(f"Duplicate header '{key}'")
701704
seen.add(kl)
702705
return header_map
703706

@@ -710,7 +713,10 @@ def _parse_headers(self, headers: list[str]) -> dict[str, str]:
710713
key, value = h.split(":", 1)
711714
header_map[key.strip()] = value.strip()
712715

713-
return self._validate_header_dict(header_map)
716+
try:
717+
return self._validate_header_dict(header_map)
718+
except ArgumentError as e:
719+
raise click.ClickException(str(e)) from e
714720

715721
def _prepare_headers(self, headers: dict[str, str] | None, bearer_token: str | None) -> str:
716722
all_headers = headers.copy() if headers else {}
@@ -720,8 +726,11 @@ def _prepare_headers(self, headers: dict[str, str] | None, bearer_token: str | N
720726
else:
721727
all_headers["Authorization"] = f"Bearer {bearer_token}"
722728

723-
validated_headers = self._validate_header_dict(all_headers)
724-
return self._curl_header_args(validated_headers)
729+
if bearer_token and "Authorization" not in (headers or {}):
730+
auth_header = {"Authorization": all_headers["Authorization"]}
731+
self._validate_header_dict(auth_header)
732+
733+
return self._curl_header_args(all_headers)
725734

726735
def _validate_bearer_token(self, token: str | None) -> str | None:
727736
if token is None:

packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client_test.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import pytest
33

44
from .client import BaseFlasherClient
5+
from jumpstarter.common.exceptions import ArgumentError
56

67

78
class MockFlasherClient(BaseFlasherClient):
@@ -14,6 +15,9 @@ def __init__(self):
1415
"MockLogger", (), {"warning": lambda msg: None, "info": lambda msg: None, "error": lambda msg: None}
1516
)()
1617

18+
def close(self):
19+
pass
20+
1721

1822
def test_validate_bearer_token_fails_invalid():
1923
"""Test bearer token validation fails with invalid tokens"""
@@ -37,3 +41,11 @@ def test_curl_header_args_handles_quotes():
3741
assert "'\"'\"'" in result
3842
assert result.startswith("-H '")
3943
assert result.endswith("'")
44+
45+
46+
def test_flash_fails_with_invalid_headers():
47+
"""Test flash method fails early with invalid headers"""
48+
client = MockFlasherClient()
49+
50+
with pytest.raises(ArgumentError, match="Invalid header name 'Invalid Header': must be an HTTP token"):
51+
client.flash("test.raw", headers={"Invalid Header": "value"})

0 commit comments

Comments
 (0)