-
-
Notifications
You must be signed in to change notification settings - Fork 398
[breaking] fix: validate sketch name #2051
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ package sketch | |
import ( | ||
"context" | ||
"errors" | ||
"regexp" | ||
|
||
"github.com/arduino/arduino-cli/arduino" | ||
"github.com/arduino/arduino-cli/arduino/globals" | ||
|
@@ -34,6 +35,10 @@ void loop() { | |
} | ||
`) | ||
|
||
// sketchNameMaxLength could be part of the regex, but it's intentionally left out for clearer error reporting | ||
var sketchNameMaxLength = 63 | ||
var sketchNameValidationRegex = regexp.MustCompile(`^[0-9a-zA-Z][0-9a-zA-Z_\.-]*$`) | ||
|
||
// NewSketch creates a new sketch via gRPC | ||
func NewSketch(ctx context.Context, req *rpc.NewSketchRequest) (*rpc.NewSketchResponse, error) { | ||
var sketchesDir string | ||
|
@@ -42,6 +47,11 @@ func NewSketch(ctx context.Context, req *rpc.NewSketchRequest) (*rpc.NewSketchRe | |
} else { | ||
sketchesDir = configuration.Settings.GetString("directories.User") | ||
} | ||
|
||
if err := validateSketchName(req.SketchName); err != nil { | ||
return nil, err | ||
} | ||
|
||
sketchDirPath := paths.New(sketchesDir).Join(req.SketchName) | ||
if err := sketchDirPath.MkdirAll(); err != nil { | ||
return nil, &arduino.CantCreateSketchError{Cause: err} | ||
|
@@ -59,3 +69,20 @@ func NewSketch(ctx context.Context, req *rpc.NewSketchRequest) (*rpc.NewSketchRe | |
|
||
return &rpc.NewSketchResponse{MainFile: sketchMainFilePath.String()}, nil | ||
} | ||
|
||
func validateSketchName(name string) error { | ||
if name == "" { | ||
return &arduino.CantCreateSketchError{Cause: errors.New(tr("sketch name cannot be empty"))} | ||
} | ||
if len(name) > sketchNameMaxLength { | ||
return &arduino.CantCreateSketchError{Cause: errors.New(tr("sketch name too long (%d characters). Maximum allowed length is %d", | ||
len(name), | ||
sketchNameMaxLength))} | ||
} | ||
if !sketchNameValidationRegex.MatchString(name) { | ||
return &arduino.CantCreateSketchError{Cause: errors.New(tr("invalid sketch name \"%s\". Required pattern %s", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This error outputs: |
||
name, | ||
sketchNameValidationRegex.String()))} | ||
} | ||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package sketch | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func Test_SketchNameWrongPattern(t *testing.T) { | ||
invalidNames := []string{ | ||
"&", | ||
"", | ||
".hello", | ||
"_hello", | ||
"-hello", | ||
"hello*", | ||
"||||||||||||||", | ||
",`hack[}attempt{];", | ||
} | ||
for _, name := range invalidNames { | ||
_, err := NewSketch(context.Background(), &commands.NewSketchRequest{ | ||
SketchName: name, | ||
SketchDir: t.TempDir(), | ||
}) | ||
require.NotNil(t, err) | ||
|
||
require.Error(t, err, `Can't create sketch: invalid sketch name "%s". Required pattern %s`, | ||
name, | ||
sketchNameValidationRegex) | ||
} | ||
} | ||
|
||
func Test_SketchNameEmpty(t *testing.T) { | ||
emptyName := "" | ||
_, err := NewSketch(context.Background(), &commands.NewSketchRequest{ | ||
SketchName: emptyName, | ||
SketchDir: t.TempDir(), | ||
}) | ||
require.NotNil(t, err) | ||
|
||
require.Error(t, err, `Can't create sketch: sketch name cannot be empty`) | ||
} | ||
|
||
func Test_SketchNameTooLong(t *testing.T) { | ||
tooLongName := make([]byte, sketchNameMaxLength+1) | ||
for i := range tooLongName { | ||
tooLongName[i] = 'a' | ||
} | ||
_, err := NewSketch(context.Background(), &commands.NewSketchRequest{ | ||
SketchName: string(tooLongName), | ||
SketchDir: t.TempDir(), | ||
}) | ||
require.NotNil(t, err) | ||
|
||
require.Error(t, err, `Can't create sketch: sketch name too long (%d characters). Maximum allowed length is %d`, | ||
len(tooLongName), | ||
sketchNameMaxLength) | ||
} | ||
|
||
func Test_SketchNameOk(t *testing.T) { | ||
lengthLimitName := make([]byte, sketchNameMaxLength) | ||
for i := range lengthLimitName { | ||
lengthLimitName[i] = 'a' | ||
} | ||
validNames := []string{ | ||
"h", | ||
"h.ello", | ||
"h..ello-world", | ||
"h..ello-world.", | ||
"hello_world__", | ||
string(lengthLimitName), | ||
} | ||
for _, name := range validNames { | ||
_, err := NewSketch(context.Background(), &commands.NewSketchRequest{ | ||
SketchName: name, | ||
SketchDir: t.TempDir(), | ||
}) | ||
require.Nil(t, err) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"sketch name too long (%d characters). Maximum allowed length is %d"
This string has 2 positional parameters, you must replace
%d
with%[1]d
and%[2]d
.This would normally not be a problem, but since it's translated into another language with
tr(...)
, the ordering of the parameters is not guaranteed to stay the same.