Skip to content

Commit 53abbba

Browse files
Copilotmikefarah
andauthored
Validate command node type and handle multiple results with debug log
Agent-Logs-Url: https://github.com/mikefarah/yq/sessions/928aabc5-ad71-41d8-94ab-403942e3f92d Co-authored-by: mikefarah <1151925+mikefarah@users.noreply.github.com>
1 parent 5ea069a commit 53abbba

File tree

3 files changed

+28
-7
lines changed

3 files changed

+28
-7
lines changed

pkg/yqlib/doc/operators/headers/system-operators.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ The `system` operator allows you to run an external command and use its output a
77
## Usage
88

99
```bash
10-
yq --null-input --enable-system-operator '.field = system("command"; "arg1")'
10+
yq --enable-system-operator --null-input '.field = system("command"; "arg1")'
1111
```
1212

1313
The operator takes:

pkg/yqlib/operator_system.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,20 @@ func resolveSystemArgs(argsNode *CandidateNode) []string {
2222
return nil
2323
}
2424

25+
func resolveCommandNode(commandNodes Context) (string, error) {
26+
if commandNodes.MatchingNodes.Front() == nil {
27+
return "", fmt.Errorf("system operator: command expression returned no results")
28+
}
29+
if commandNodes.MatchingNodes.Len() > 1 {
30+
log.Debugf("system operator: command expression returned %d results, using first", commandNodes.MatchingNodes.Len())
31+
}
32+
cmdNode := commandNodes.MatchingNodes.Front().Value.(*CandidateNode)
33+
if cmdNode.Kind != ScalarNode || cmdNode.Tag == "!!null" {
34+
return "", fmt.Errorf("system operator: command must be a string scalar")
35+
}
36+
return cmdNode.Value, nil
37+
}
38+
2539
func systemOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
2640
if !ConfiguredSecurityPreferences.EnableSystemOps {
2741
log.Warning("system operator is disabled, use --enable-system-operator flag to enable")
@@ -51,10 +65,10 @@ func systemOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
5165
if err != nil {
5266
return Context{}, err
5367
}
54-
if commandNodes.MatchingNodes.Front() == nil {
55-
return Context{}, fmt.Errorf("system operator: command expression returned no results")
68+
command, err = resolveCommandNode(commandNodes)
69+
if err != nil {
70+
return Context{}, err
5671
}
57-
command = commandNodes.MatchingNodes.Front().Value.(*CandidateNode).Value
5872

5973
argsNodes, err := d.GetMatchingNodes(nodeContext, block.RHS)
6074
if err != nil {
@@ -68,10 +82,10 @@ func systemOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
6882
if err != nil {
6983
return Context{}, err
7084
}
71-
if commandNodes.MatchingNodes.Front() == nil {
72-
return Context{}, fmt.Errorf("system operator: command expression returned no results")
85+
command, err = resolveCommandNode(commandNodes)
86+
if err != nil {
87+
return Context{}, err
7388
}
74-
command = commandNodes.MatchingNodes.Front().Value.(*CandidateNode).Value
7589
}
7690

7791
var stdin bytes.Buffer

pkg/yqlib/operator_system_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,13 @@ func TestSystemOperatorEnabledScenarios(t *testing.T) {
9898
expression: `.a = system("` + falsePath + `")`,
9999
expectedError: "system command '" + falsePath + "' failed: exit status 1",
100100
},
101+
{
102+
description: "Null command returns error",
103+
skipDoc: true,
104+
document: "a: hello",
105+
expression: `.a = system(null)`,
106+
expectedError: "system operator: command must be a string scalar",
107+
},
101108
}
102109

103110
for _, tt := range scenarios {

0 commit comments

Comments
 (0)