Skip to content

Commit e0b44ee

Browse files
committed
Reject node labels with shell metacharacters in CLI validate (#251)
1 parent 2a53537 commit e0b44ee

File tree

2 files changed

+22
-0
lines changed

2 files changed

+22
-0
lines changed

concore_cli/commands/validate.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ def validate_workflow(workflow_file, console):
8484
label = label_tag.text.strip()
8585
node_labels.append(label)
8686

87+
# reject shell metacharacters to prevent command injection (#251)
88+
if re.search(r'[;&|`$\'"()\\]', label):
89+
errors.append(f"Node '{label}' contains unsafe shell characters")
90+
continue
91+
8792
if ':' not in label:
8893
warnings.append(f"Node '{label}' missing format 'ID:filename'")
8994
else:

tests/test_graph.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,23 @@ def test_validate_node_missing_filename(self):
112112
self.assertIn('Validation failed', result.output)
113113
self.assertIn('has no filename', result.output)
114114

115+
def test_validate_unsafe_node_label(self):
116+
content = '''
117+
<graphml xmlns:y="http://www.yworks.com/xml/graphml">
118+
<graph id="G" edgedefault="directed">
119+
<node id="n0">
120+
<data key="d0"><y:NodeLabel>n0;rm -rf /:script.py</y:NodeLabel></data>
121+
</node>
122+
</graph>
123+
</graphml>
124+
'''
125+
filepath = self.create_graph_file('injection.graphml', content)
126+
127+
result = self.runner.invoke(cli, ['validate', filepath])
128+
129+
self.assertIn('Validation failed', result.output)
130+
self.assertIn('unsafe shell characters', result.output)
131+
115132
def test_validate_valid_graph(self):
116133
content = '''
117134
<graphml xmlns:y="http://www.yworks.com/xml/graphml">

0 commit comments

Comments
 (0)