Skip to content

Commit

Permalink
feat: validate cli request (abcxyz#50)
Browse files Browse the repository at this point in the history
  • Loading branch information
sqin2019 authored Jun 23, 2023
1 parent e6a4384 commit 490e6a9
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 0 deletions.
50 changes: 50 additions & 0 deletions apis/v1alpha1/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,22 @@
package v1alpha1

import (
"bufio"
"errors"
"fmt"
"net/mail"
"strings"
)

var (
defaultCLI = "gcloud"
invalidCommandOperators = map[rune]struct{}{
'&': {},
'|': {},
'>': {},
}
)

// ValidateIAMRequest checks if the IAMRequest is valid.
func ValidateIAMRequest(r *IAMRequest) (retErr error) {
for _, s := range r.ResourcePolicies {
Expand Down Expand Up @@ -57,3 +67,43 @@ func ValidateIAMRequest(r *IAMRequest) (retErr error) {
}
return
}

// ValidateCLIRequest checks if the CLIRequest is valid.
func ValidateCLIRequest(r *CLIRequest) (retErr error) {
// Set default CLI
if r.CLI == "" {
r.CLI = defaultCLI
}
// TODO (#49): support other CLIs.
if r.CLI != defaultCLI {
retErr = errors.Join(retErr, fmt.Errorf("CLI %q is not supported", r.CLI))
}

// Check if the do commands are valid.
for _, c := range r.Do {
if err := checkCommand(c); err != nil {
retErr = errors.Join(retErr, fmt.Errorf("do command %q is not valid: %w", c, err))
}
}

// Check if the cleanup commands are valid.
for _, c := range r.Cleanup {
if err := checkCommand(c); err != nil {
retErr = errors.Join(retErr, fmt.Errorf("cleanup command %q is not valid: %w", c, err))
}
}
return retErr
}

func checkCommand(c string) (retErr error) {
scanner := bufio.NewScanner(strings.NewReader(c))
for row := 1; scanner.Scan(); row++ {
line := scanner.Text()
for col, r := range line {
if _, ok := invalidCommandOperators[r]; ok {
retErr = errors.Join(retErr, fmt.Errorf("disallowed command character %q at %d:%d", r, row, col))
}
}
}
return retErr
}
90 changes: 90 additions & 0 deletions apis/v1alpha1/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,93 @@ func TestValidateIAMRequest(t *testing.T) {
})
}
}

func TestValidateCLIRequest(t *testing.T) {
t.Parallel()

cases := []struct {
name string
request *CLIRequest
wantErr string
}{
{
name: "success",
request: &CLIRequest{
CLI: "gcloud",
Do: []string{
"run jobs execute my-job1",
"run jobs execute my-job2",
},
Cleanup: []string{
"run jobs executions delete my-execution1",
"run jobs executions delete my-execution2",
},
},
},
{
name: "success_with_default_cli",
request: &CLIRequest{
Do: []string{
"run jobs execute my-job1",
"run jobs execute my-job2",
},
Cleanup: []string{
"run jobs executions delete my-execution1",
"run jobs executions delete my-execution2",
},
},
},
{
name: "invalid_cli",
request: &CLIRequest{
CLI: "aws",
Do: []string{
"run jobs execute my-job",
},
Cleanup: []string{
"run jobs executions delete my-execution",
},
},
wantErr: `CLI "aws" is not supported`,
},
{
name: "invalid_do_command",
request: &CLIRequest{
Do: []string{
`run
jobs execute my-job && rmdir dir`,
},
Cleanup: []string{
"run jobs executions delete my-execution",
},
},
wantErr: `disallowed command character '&' at 2:20
disallowed command character '&' at 2:21`,
},
{
name: "invalid_cleanup_command",
request: &CLIRequest{
Do: []string{
"run jobs execute my-job",
},
Cleanup: []string{
"storage cat gs://bucket/secrets.txt > my-file.txt",
},
},
wantErr: `disallowed command character '>' at 1:36`,
},
}

for _, tc := range cases {
tc := tc

t.Run(tc.name, func(t *testing.T) {
t.Parallel()

gotErr := ValidateCLIRequest(tc.request)
if diff := testutil.DiffErrString(gotErr, tc.wantErr); diff != "" {
t.Errorf("Process %s got unexpected error: %s", tc.name, diff)
}
})
}
}

0 comments on commit 490e6a9

Please sign in to comment.