Skip to content

Commit cd207a6

Browse files
szachovyCopilot
andcommitted
Fix Python code injection via hostname interpolation
Anchor hostname regex with ^ and $ to prevent partial matches. Use repr() for all values interpolated into Python source strings passed to run_python_container_command(). Prevents string literal breakout via crafted hostnames or parameters. Closes #86 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 0ab8ce7 commit cd207a6

2 files changed

Lines changed: 29 additions & 25 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616
* CI workflow to build and push service images to GitHub Container Registry on master merge. (#97)
1717
* Pull-first with local build fallback for container images during deployment. (#97)
1818

19+
### Fixed
20+
21+
* Anchor hostname regex and use repr() for all string-interpolated remote exec values. (#86)
22+
1923
### Changed
2024

2125
* Completed [ARCHITECTURE.md](./docs/ARCHITECTURE.md) (#93)

src/initialize.py

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,9 @@ def validate_nodes(self) -> None:
9494

9595
def validate_hostname(hostname: str) -> str:
9696
"""(RFC 1123 compliance)."""
97-
allowed_characters = re.compile(r"[A-Z\d-]{1,63}", re.IGNORECASE)
97+
allowed_characters = re.compile(r"^[A-Z\d-]{1,63}$", re.IGNORECASE)
9898
if not ((len(hostname) > 255) or ("." in hostname)):
99-
if all(allowed_characters.match(x) for x in hostname):
99+
if allowed_characters.match(hostname):
100100
return hostname
101101
raise ValueError("Invalid node hostname provided")
102102

@@ -152,7 +152,7 @@ def get_mylogin_cnf(self, node: remote.RemoteConnection) -> bytes:
152152
{{'MYSQL_TEST_LOGIN_FILE': '/var/run/mysqld/.mylogin.cnf'}} \
153153
) \
154154
)".format(
155-
mysql_nodes=" ".join(node.node for node in self.mysql_nodes))
155+
mysql_nodes=" ".join(repr(node.node) for node in self.mysql_nodes))
156156
)["output"]
157157
mylogin_cnf = base64.b64decode(output[2:-2].replace("\\n", ""))
158158
if len(mylogin_cnf) > 320:
@@ -228,22 +228,22 @@ def start_mysql_mgmt(self, node: remote.RemoteConnection, state: str, priority:
228228
"ContainerConnection( \
229229
container='mysql-mgmt' \
230230
).run_mysql_mgmt( \
231-
'{virtual_ip_address}', \
232-
'{virtual_network_mask}', \
233-
'{virtual_network_interface}', \
234-
'{primary_mysql_node}', \
235-
'{secondary_first_mysql_node}', \
236-
'{secondary_second_mysql_node}', \
237-
'{state}', \
238-
'{priority}' \
239-
)".format(virtual_ip_address=self.virtual_ip_address,
240-
virtual_network_mask=self.virtual_network_mask,
241-
virtual_network_interface=self.virtual_network_interface,
242-
primary_mysql_node=self.mysql_nodes[0].node,
243-
secondary_first_mysql_node=self.mysql_nodes[1].node,
244-
secondary_second_mysql_node=self.mysql_nodes[2].node,
245-
state=state,
246-
priority=priority)
231+
{virtual_ip_address}, \
232+
{virtual_network_mask}, \
233+
{virtual_network_interface}, \
234+
{primary_mysql_node}, \
235+
{secondary_first_mysql_node}, \
236+
{secondary_second_mysql_node}, \
237+
{state}, \
238+
{priority} \
239+
)".format(virtual_ip_address=repr(self.virtual_ip_address),
240+
virtual_network_mask=repr(str(self.virtual_network_mask)),
241+
virtual_network_interface=repr(self.virtual_network_interface),
242+
primary_mysql_node=repr(self.mysql_nodes[0].node),
243+
secondary_first_mysql_node=repr(self.mysql_nodes[1].node),
244+
secondary_second_mysql_node=repr(self.mysql_nodes[2].node),
245+
state=repr(state),
246+
priority=repr(str(priority)))
247247
)
248248

249249
def start_superset(self) -> None:
@@ -272,12 +272,12 @@ def start_superset(self) -> None:
272272
"ContainerConnection( \
273273
container='superset' \
274274
).run_superset( \
275-
'{virtual_ip_address}', \
276-
'{superset_secret_key}', \
277-
'{mysql_superset_password}' \
278-
)".format(virtual_ip_address=self.virtual_ip_address,
279-
superset_secret_key=self.superset_secret_key,
280-
mysql_superset_password=self.mysql_superset_password)
275+
{virtual_ip_address}, \
276+
{superset_secret_key}, \
277+
{mysql_superset_password} \
278+
)".format(virtual_ip_address=repr(self.virtual_ip_address),
279+
superset_secret_key=repr(self.superset_secret_key),
280+
mysql_superset_password=repr(self.mysql_superset_password))
281281
)
282282

283283
def start_cluster(self) -> None:

0 commit comments

Comments
 (0)