Skip to content

Commit 84919e1

Browse files
authored
Merge pull request #60 from arduino/per1234/disallowed-characters-in-base-name-check
Add checks for disallowed characters in library and sketch base names
2 parents d843b80 + 997f76e commit 84919e1

File tree

13 files changed

+236
-36
lines changed

13 files changed

+236
-36
lines changed

check/checkconfigurations/checkconfigurations.go

+30
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,36 @@ var configurations = []Type{
851851
ErrorModes: nil,
852852
CheckFunction: checkfunctions.LibraryHasExe,
853853
},
854+
{
855+
ProjectType: projecttype.Library,
856+
Category: "structure",
857+
Subcategory: "",
858+
ID: "",
859+
Brief: "disallowed characters in folder name",
860+
Description: "This will be problematic for people doing manual installation of the library.",
861+
MessageTemplate: "Prohibited characters in folder name: {{.}}. See: https://arduino.github.io/arduino-cli/latest/library-specification/#library-root-folder",
862+
DisableModes: nil,
863+
EnableModes: []checkmode.Type{checkmode.All},
864+
InfoModes: nil,
865+
WarningModes: []checkmode.Type{checkmode.Permissive},
866+
ErrorModes: []checkmode.Type{checkmode.Default},
867+
CheckFunction: checkfunctions.ProhibitedCharactersInLibraryFolderName,
868+
},
869+
{
870+
ProjectType: projecttype.Sketch,
871+
Category: "structure",
872+
Subcategory: "",
873+
ID: "",
874+
Brief: "disallowed characters in file name",
875+
Description: "",
876+
MessageTemplate: "Prohibited characters in file name(s): {{.}}. See: https://arduino.github.io/arduino-cli/latest/sketch-specification/#sketch-root-folder",
877+
DisableModes: nil,
878+
EnableModes: []checkmode.Type{checkmode.All},
879+
InfoModes: nil,
880+
WarningModes: nil,
881+
ErrorModes: []checkmode.Type{checkmode.Default},
882+
CheckFunction: checkfunctions.ProhibitedCharactersInSketchFileName,
883+
},
854884
{
855885
ProjectType: projecttype.Sketch,
856886
Category: "structure",

check/checkfunctions/checkfunctions.go

+8
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,17 @@
1717
package checkfunctions
1818

1919
import (
20+
"regexp"
21+
2022
"github.com/arduino/arduino-check/check/checkresult"
2123
)
2224

2325
// Type is the function signature for the check functions.
2426
// The `output` result is the contextual information that will be inserted into the check's message template.
2527
type Type func() (result checkresult.Type, output string)
28+
29+
// validProjectPathBaseName checks whether the provided library folder or sketch filename contains prohibited characters.
30+
func validProjectPathBaseName(name string) bool {
31+
baseNameRegexp := regexp.MustCompile("^[a-zA-Z0-9][a-zA-Z0-9_.-]*$")
32+
return baseNameRegexp.MatchString(name)
33+
}

check/checkfunctions/library.go

+9
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,15 @@ func LibraryHasExe() (result checkresult.Type, output string) {
929929
return checkresult.Pass, ""
930930
}
931931

932+
// ProhibitedCharactersInLibraryFolderName checks for prohibited characters in the library folder name.
933+
func ProhibitedCharactersInLibraryFolderName() (result checkresult.Type, output string) {
934+
if !validProjectPathBaseName(checkdata.ProjectPath().Base()) {
935+
return checkresult.Fail, checkdata.ProjectPath().Base()
936+
}
937+
938+
return checkresult.Pass, ""
939+
}
940+
932941
// spellCheckLibraryPropertiesFieldValue returns the value of the provided library.properties field with commonly misspelled words corrected.
933942
func spellCheckLibraryPropertiesFieldValue(fieldName string) (result checkresult.Type, output string) {
934943
if checkdata.LibraryPropertiesLoadError() != nil {

check/checkfunctions/library_test.go

+44-36
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ func init() {
3838
schemasPath = paths.New(workingDirectory, "..", "..", "etc", "schemas")
3939
}
4040

41-
type checkFunctionTestTable struct {
41+
type libraryCheckFunctionTestTable struct {
4242
testName string
4343
libraryFolderName string
4444
expectedCheckResult checkresult.Type
4545
expectedOutputQuery string
4646
}
4747

48-
func checkCheckFunction(checkFunction Type, testTables []checkFunctionTestTable, t *testing.T) {
48+
func checkLibraryCheckFunction(checkFunction Type, testTables []libraryCheckFunctionTestTable, t *testing.T) {
4949
for _, testTable := range testTables {
5050
expectedOutputRegexp := regexp.MustCompile(testTable.expectedOutputQuery)
5151

@@ -64,124 +64,124 @@ func checkCheckFunction(checkFunction Type, testTables []checkFunctionTestTable,
6464
}
6565

6666
func TestLibraryPropertiesNameFieldMissingOfficialPrefix(t *testing.T) {
67-
testTables := []checkFunctionTestTable{
67+
testTables := []libraryCheckFunctionTestTable{
6868
{"Unable to load", "InvalidLibraryProperties", checkresult.NotRun, ""},
6969
{"Not defined", "MissingFields", checkresult.NotRun, ""},
7070
{"Correct prefix", "Arduino_Official", checkresult.Pass, ""},
7171
{"Incorrect prefix", "Recursive", checkresult.Fail, "^Recursive$"},
7272
}
7373

74-
checkCheckFunction(LibraryPropertiesNameFieldMissingOfficialPrefix, testTables, t)
74+
checkLibraryCheckFunction(LibraryPropertiesNameFieldMissingOfficialPrefix, testTables, t)
7575
}
7676

7777
func TestLibraryPropertiesNameFieldDuplicate(t *testing.T) {
78-
testTables := []checkFunctionTestTable{
78+
testTables := []libraryCheckFunctionTestTable{
7979
{"Unable to load", "InvalidLibraryProperties", checkresult.NotRun, ""},
8080
{"Duplicate", "Indexed", checkresult.Fail, "^Servo$"},
8181
{"Not duplicate", "NotIndexed", checkresult.Pass, ""},
8282
}
8383

84-
checkCheckFunction(LibraryPropertiesNameFieldDuplicate, testTables, t)
84+
checkLibraryCheckFunction(LibraryPropertiesNameFieldDuplicate, testTables, t)
8585
}
8686

8787
func TestLibraryPropertiesNameFieldNotInIndex(t *testing.T) {
88-
testTables := []checkFunctionTestTable{
88+
testTables := []libraryCheckFunctionTestTable{
8989
{"Unable to load", "InvalidLibraryProperties", checkresult.NotRun, ""},
9090
{"In index", "Indexed", checkresult.Pass, ""},
9191
{"Not in index", "NotIndexed", checkresult.Fail, "^NotIndexed$"},
9292
}
9393

94-
checkCheckFunction(LibraryPropertiesNameFieldNotInIndex, testTables, t)
94+
checkLibraryCheckFunction(LibraryPropertiesNameFieldNotInIndex, testTables, t)
9595
}
9696

9797
func TestLibraryPropertiesNameFieldHeaderMismatch(t *testing.T) {
98-
testTables := []checkFunctionTestTable{
98+
testTables := []libraryCheckFunctionTestTable{
9999
{"Unable to load", "InvalidLibraryProperties", checkresult.NotRun, ""},
100100
{"Mismatch", "NameHeaderMismatch", checkresult.Fail, "^NameHeaderMismatch.h$"},
101101
{"Match", "Recursive", checkresult.Pass, ""},
102102
}
103103

104-
checkCheckFunction(LibraryPropertiesNameFieldHeaderMismatch, testTables, t)
104+
checkLibraryCheckFunction(LibraryPropertiesNameFieldHeaderMismatch, testTables, t)
105105
}
106106

107107
func TestLibraryPropertiesSentenceFieldSpellCheck(t *testing.T) {
108-
testTables := []checkFunctionTestTable{
108+
testTables := []libraryCheckFunctionTestTable{
109109
{"Unable to load", "InvalidLibraryProperties", checkresult.NotRun, ""},
110110
{"Not defined", "MissingFields", checkresult.NotRun, ""},
111111
{"Misspelled word", "MisspelledSentenceParagraphValue", checkresult.Fail, "^grill broccoli now$"},
112112
{"Correct spelling", "Recursive", checkresult.Pass, ""},
113113
}
114114

115-
checkCheckFunction(LibraryPropertiesSentenceFieldSpellCheck, testTables, t)
115+
checkLibraryCheckFunction(LibraryPropertiesSentenceFieldSpellCheck, testTables, t)
116116
}
117117

118118
func TestLibraryPropertiesParagraphFieldSpellCheck(t *testing.T) {
119-
testTables := []checkFunctionTestTable{
119+
testTables := []libraryCheckFunctionTestTable{
120120
{"Unable to load", "InvalidLibraryProperties", checkresult.NotRun, ""},
121121
{"Not defined", "MissingFields", checkresult.NotRun, ""},
122122
{"Misspelled word", "MisspelledSentenceParagraphValue", checkresult.Fail, "^There is a zebra$"},
123123
{"Correct spelling", "Recursive", checkresult.Pass, ""},
124124
}
125125

126-
checkCheckFunction(LibraryPropertiesParagraphFieldSpellCheck, testTables, t)
126+
checkLibraryCheckFunction(LibraryPropertiesParagraphFieldSpellCheck, testTables, t)
127127
}
128128

129129
func TestLibraryPropertiesParagraphFieldRepeatsSentence(t *testing.T) {
130-
testTables := []checkFunctionTestTable{
130+
testTables := []libraryCheckFunctionTestTable{
131131
{"Unable to load", "InvalidLibraryProperties", checkresult.NotRun, ""},
132132
{"Repeat", "ParagraphRepeatsSentence", checkresult.Fail, ""},
133133
{"No repeat", "Recursive", checkresult.Pass, ""},
134134
}
135135

136-
checkCheckFunction(LibraryPropertiesParagraphFieldRepeatsSentence, testTables, t)
136+
checkLibraryCheckFunction(LibraryPropertiesParagraphFieldRepeatsSentence, testTables, t)
137137
}
138138
func TestLibraryPropertiesUrlFieldDeadLink(t *testing.T) {
139-
testTables := []checkFunctionTestTable{
139+
testTables := []libraryCheckFunctionTestTable{
140140
{"Unable to load", "InvalidLibraryProperties", checkresult.NotRun, ""},
141141
{"Not defined", "MissingFields", checkresult.NotRun, ""},
142142
{"Bad URL", "BadURL", checkresult.Fail, "^Get \"http://invalid/\": dial tcp: lookup invalid:"},
143143
{"HTTP error 404", "URL404", checkresult.Fail, "^404 Not Found$"},
144144
{"Good URL", "Recursive", checkresult.Pass, ""},
145145
}
146146

147-
checkCheckFunction(LibraryPropertiesUrlFieldDeadLink, testTables, t)
147+
checkLibraryCheckFunction(LibraryPropertiesUrlFieldDeadLink, testTables, t)
148148
}
149149

150150
func TestLibraryPropertiesDependsFieldNotInIndex(t *testing.T) {
151-
testTables := []checkFunctionTestTable{
151+
testTables := []libraryCheckFunctionTestTable{
152152
{"Unable to load", "InvalidLibraryProperties", checkresult.NotRun, ""},
153153
{"Dependency not in index", "DependsNotIndexed", checkresult.Fail, "^NotIndexed$"},
154154
{"Dependency in index", "DependsIndexed", checkresult.Pass, ""},
155155
{"No depends", "NoDepends", checkresult.NotRun, ""},
156156
}
157157

158-
checkCheckFunction(LibraryPropertiesDependsFieldNotInIndex, testTables, t)
158+
checkLibraryCheckFunction(LibraryPropertiesDependsFieldNotInIndex, testTables, t)
159159
}
160160

161161
func TestLibraryPropertiesDotALinkageFieldTrueWithFlatLayout(t *testing.T) {
162-
testTables := []checkFunctionTestTable{
162+
testTables := []libraryCheckFunctionTestTable{
163163
{"Unable to load", "InvalidLibraryProperties", checkresult.NotRun, ""},
164164
{"Not defined", "MissingFields", checkresult.NotRun, ""},
165165
{"Flat layout", "DotALinkageFlat", checkresult.Fail, ""},
166166
{"Recursive layout", "DotALinkage", checkresult.Pass, ""},
167167
}
168168

169-
checkCheckFunction(LibraryPropertiesDotALinkageFieldTrueWithFlatLayout, testTables, t)
169+
checkLibraryCheckFunction(LibraryPropertiesDotALinkageFieldTrueWithFlatLayout, testTables, t)
170170
}
171171

172172
func TestLibraryPropertiesIncludesFieldItemNotFound(t *testing.T) {
173-
testTables := []checkFunctionTestTable{
173+
testTables := []libraryCheckFunctionTestTable{
174174
{"Unable to load", "InvalidLibraryProperties", checkresult.NotRun, ""},
175175
{"Not defined", "MissingFields", checkresult.NotRun, ""},
176176
{"Missing includes", "MissingIncludes", checkresult.Fail, "^Nonexistent.h$"},
177177
{"Present includes", "Recursive", checkresult.Pass, ""},
178178
}
179179

180-
checkCheckFunction(LibraryPropertiesIncludesFieldItemNotFound, testTables, t)
180+
checkLibraryCheckFunction(LibraryPropertiesIncludesFieldItemNotFound, testTables, t)
181181
}
182182

183183
func TestLibraryPropertiesPrecompiledFieldEnabledWithFlatLayout(t *testing.T) {
184-
testTables := []checkFunctionTestTable{
184+
testTables := []libraryCheckFunctionTestTable{
185185
{"Unable to load", "InvalidLibraryProperties", checkresult.NotRun, ""},
186186
{"Not defined", "MissingFields", checkresult.NotRun, ""},
187187
{"Flat layout", "PrecompiledFlat", checkresult.Fail, "^true$"},
@@ -190,16 +190,16 @@ func TestLibraryPropertiesPrecompiledFieldEnabledWithFlatLayout(t *testing.T) {
190190
{"Flat, not precompiled", "NotPrecompiledFlat", checkresult.NotRun, ""},
191191
}
192192

193-
checkCheckFunction(LibraryPropertiesPrecompiledFieldEnabledWithFlatLayout, testTables, t)
193+
checkLibraryCheckFunction(LibraryPropertiesPrecompiledFieldEnabledWithFlatLayout, testTables, t)
194194
}
195195

196196
func TestLibraryHasSubmodule(t *testing.T) {
197-
testTables := []checkFunctionTestTable{
197+
testTables := []libraryCheckFunctionTestTable{
198198
{"Has submodule", "Submodule", checkresult.Fail, ""},
199199
{"No submodule", "Recursive", checkresult.Pass, ""},
200200
}
201201

202-
checkCheckFunction(LibraryHasSubmodule, testTables, t)
202+
checkLibraryCheckFunction(LibraryHasSubmodule, testTables, t)
203203
}
204204

205205
func TestLibraryContainsSymlinks(t *testing.T) {
@@ -210,36 +210,44 @@ func TestLibraryContainsSymlinks(t *testing.T) {
210210
require.Nil(t, err, "This test must be run as administrator on Windows to have symlink creation privilege.")
211211
defer symlinkPath.RemoveAll() // clean up
212212

213-
testTables := []checkFunctionTestTable{
213+
testTables := []libraryCheckFunctionTestTable{
214214
{"Has symlink", testLibrary, checkresult.Fail, ""},
215215
}
216216

217-
checkCheckFunction(LibraryContainsSymlinks, testTables, t)
217+
checkLibraryCheckFunction(LibraryContainsSymlinks, testTables, t)
218218

219219
err = symlinkPath.RemoveAll()
220220
require.Nil(t, err)
221221

222-
testTables = []checkFunctionTestTable{
222+
testTables = []libraryCheckFunctionTestTable{
223223
{"No symlink", testLibrary, checkresult.Pass, ""},
224224
}
225225

226-
checkCheckFunction(LibraryContainsSymlinks, testTables, t)
226+
checkLibraryCheckFunction(LibraryContainsSymlinks, testTables, t)
227227
}
228228

229229
func TestLibraryHasDotDevelopmentFile(t *testing.T) {
230-
testTables := []checkFunctionTestTable{
230+
testTables := []libraryCheckFunctionTestTable{
231231
{"Has .development file", "DotDevelopment", checkresult.Fail, ""},
232232
{"No .development file", "Recursive", checkresult.Pass, ""},
233233
}
234234

235-
checkCheckFunction(LibraryHasDotDevelopmentFile, testTables, t)
235+
checkLibraryCheckFunction(LibraryHasDotDevelopmentFile, testTables, t)
236236
}
237237

238238
func TestLibraryHasExe(t *testing.T) {
239-
testTables := []checkFunctionTestTable{
239+
testTables := []libraryCheckFunctionTestTable{
240240
{"Has .exe file", "Exe", checkresult.Fail, ""},
241241
{"No .exe files", "Recursive", checkresult.Pass, ""},
242242
}
243243

244-
checkCheckFunction(LibraryHasExe, testTables, t)
244+
checkLibraryCheckFunction(LibraryHasExe, testTables, t)
245+
}
246+
func TestProhibitedCharactersInLibraryFolderName(t *testing.T) {
247+
testTables := []libraryCheckFunctionTestTable{
248+
{"Has prohibited characters", "Prohibited CharactersInFolderName", checkresult.Fail, ""},
249+
{"No prohibited characters", "Recursive", checkresult.Pass, ""},
250+
}
251+
252+
checkLibraryCheckFunction(ProhibitedCharactersInLibraryFolderName, testTables, t)
245253
}

check/checkfunctions/sketch.go

+22
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,30 @@ import (
2222

2323
"github.com/arduino/arduino-check/check/checkdata"
2424
"github.com/arduino/arduino-check/check/checkresult"
25+
"github.com/arduino/arduino-check/project/sketch"
2526
)
2627

28+
// ProhibitedCharactersInSketchFileName checks for prohibited characters in the sketch file names.
29+
func ProhibitedCharactersInSketchFileName() (result checkresult.Type, output string) {
30+
directoryListing, _ := checkdata.ProjectPath().ReadDir()
31+
directoryListing.FilterOutDirs()
32+
33+
foundInvalidSketchFileNames := []string{}
34+
for _, potentialSketchFile := range directoryListing {
35+
if sketch.HasSupportedExtension(potentialSketchFile) {
36+
if !validProjectPathBaseName(potentialSketchFile.Base()) {
37+
foundInvalidSketchFileNames = append(foundInvalidSketchFileNames, potentialSketchFile.Base())
38+
}
39+
}
40+
}
41+
42+
if len(foundInvalidSketchFileNames) > 0 {
43+
return checkresult.Fail, strings.Join(foundInvalidSketchFileNames, ", ")
44+
}
45+
46+
return checkresult.Pass, ""
47+
}
48+
2749
// PdeSketchExtension checks for use of deprecated .pde sketch file extensions.
2850
func PdeSketchExtension() (result checkresult.Type, output string) {
2951
directoryListing, _ := checkdata.ProjectPath().ReadDir()

0 commit comments

Comments
 (0)