Skip to content

Commit 66a9d6d

Browse files
[skip-changelog] Improve the definition of FQBN and explicitly state which characters are allowed (#2509)
* Improve the definition of FQBN and explicitly state which characters are allowed * Enforce fqbn characters validation * Test that an error is returned when the FQBN contains an invalid character
1 parent ec157a8 commit 66a9d6d

File tree

3 files changed

+36
-1
lines changed

3 files changed

+36
-1
lines changed

docs/FAQ.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ When you run [`arduino-cli board list`][arduino cli board list], your board does
99

1010
## What's the FQBN string?
1111

12-
For a deeper understanding of how FQBN works, you should understand the [Arduino platform specification][0].
12+
FQBN stands for Fully Qualified Board Name. It has the following format:
13+
`VENDOR:ARCHITECTURE:BOARD_ID[:MENU_ID=OPTION_ID[,MENU2_ID=OPTION_ID ...]]`, with each `MENU_ID=OPTION_ID` being an
14+
optional key-value pair configuration. Each field accepts letters (`A-Z` or `a-z`), numbers (`0-9`), underscores (`_`),
15+
dashes(`-`) and dots(`.`). The special character `=` is accepted in the configuration value. For a deeper understanding
16+
of how FQBN works, you should understand the [Arduino platform specification][0].
1317

1418
## How to set multiple board options?
1519

internal/arduino/cores/fqbn.go

+16
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package cores
1717

1818
import (
1919
"fmt"
20+
"regexp"
2021
"strings"
2122

2223
properties "github.com/arduino/go-properties-orderedmap"
@@ -57,6 +58,13 @@ func ParseFQBN(fqbnIn string) (*FQBN, error) {
5758
if fqbn.BoardID == "" {
5859
return nil, fmt.Errorf(tr("empty board identifier"))
5960
}
61+
// Check if the fqbn contains invalid characters
62+
fqbnValidationRegex := regexp.MustCompile(`^[a-zA-Z0-9_.-]*$`)
63+
for i := 0; i < 3; i++ {
64+
if !fqbnValidationRegex.MatchString(fqbnParts[i]) {
65+
return nil, fmt.Errorf(tr("fqbn's field %s contains an invalid character"), fqbnParts[i])
66+
}
67+
}
6068
if len(fqbnParts) > 3 {
6169
for _, pair := range strings.Split(fqbnParts[3], ",") {
6270
parts := strings.SplitN(pair, "=", 2)
@@ -68,6 +76,14 @@ func ParseFQBN(fqbnIn string) (*FQBN, error) {
6876
if k == "" {
6977
return nil, fmt.Errorf(tr("invalid config option: %s"), pair)
7078
}
79+
if !fqbnValidationRegex.MatchString(k) {
80+
return nil, fmt.Errorf(tr("config key %s contains an invalid character"), k)
81+
}
82+
// The config value can also contain the = symbol
83+
valueValidationRegex := regexp.MustCompile(`^[a-zA-Z0-9=_.-]*$`)
84+
if !valueValidationRegex.MatchString(v) {
85+
return nil, fmt.Errorf(tr("config value %s contains an invalid character"), v)
86+
}
7187
fqbn.Configs.Set(k, v)
7288
}
7389
}

internal/arduino/cores/fqbn_test.go

+15
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,18 @@ func TestMatch(t *testing.T) {
155155
require.False(t, b.Match(a))
156156
}
157157
}
158+
159+
func TestValidCharacters(t *testing.T) {
160+
// These FQBNs contain valid characters
161+
validFqbns := []string{"ardui_no:av_r:un_o", "arduin.o:av.r:un.o", "arduin-o:av-r:un-o", "arduin-o:av-r:un-o:a=b=c=d"}
162+
for _, fqbn := range validFqbns {
163+
_, err := ParseFQBN(fqbn)
164+
require.NoError(t, err)
165+
}
166+
// These FQBNs contain invalid characters
167+
invalidFqbns := []string{"arduin-o:av-r:un=o", "arduin?o:av-r:uno", "arduino:av*r:uno"}
168+
for _, fqbn := range invalidFqbns {
169+
_, err := ParseFQBN(fqbn)
170+
require.Error(t, err)
171+
}
172+
}

0 commit comments

Comments
 (0)