Skip to content

Commit

Permalink
Merge pull request #1449 from adamdecaf/expand-allowed-characters
Browse files Browse the repository at this point in the history
fix: expand which latin characters are accepted
  • Loading branch information
adamdecaf authored Jul 18, 2024
2 parents fc02648 + d5894e1 commit c39e176
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 57 deletions.
2 changes: 1 addition & 1 deletion fileHeader.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ func (fh *FileHeader) ValidateWith(opts *ValidateOpts) error {
if err := fh.fieldInclusion(); err != nil {
return err
}
if err := fh.isUpperAlphanumeric(fh.FileIDModifier); err != nil {
if err := fh.isUpperASCII(fh.FileIDModifier); err != nil {
return fieldError("FileIDModifier", err, fh.FileIDModifier)
}
if len(fh.FileIDModifier) != 1 {
Expand Down
78 changes: 29 additions & 49 deletions validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,29 +28,6 @@ import (
"unicode/utf8"
)

var (
lowerAlphaCharacters = "abcdefghijklmnopqrstuvwxyz"
numericCharacters = "0123456789"
asciiCharacters = ` !"#$%&'()*+,-./:;<=>?@[\]^_{|}~` + "`"
ebcdicExtraCharacters = `¢¬¦±`
realWorldEncountered = `Ø`

validAlphaNumericCharacters map[rune]bool
validUppercaseAlphaNumericCharacters map[rune]bool
)

func init() {
validAlphaNumericCharacters = setupCharacterMap(
lowerAlphaCharacters, strings.ToUpper(lowerAlphaCharacters), numericCharacters, asciiCharacters,
ebcdicExtraCharacters, realWorldEncountered,
)

validUppercaseAlphaNumericCharacters = setupCharacterMap(
strings.ToUpper(lowerAlphaCharacters), numericCharacters, asciiCharacters,
ebcdicExtraCharacters, realWorldEncountered,
)
}

// validator is common validation and formatting of golang types to ach type strings
type validator struct{}

Expand Down Expand Up @@ -438,40 +415,43 @@ func (v *validator) isTransactionTypeCode(s string) error {
return ErrTransactionTypeCode
}

func setupCharacterMap(inputs ...string) map[rune]bool {
out := make(map[rune]bool)
for _, input := range inputs {
for _, r := range input {
out[r] = true
}
}
return out
}

func (v *validator) includesValidCharacters(input string, charset map[rune]bool) error {
for _, i := range input {
_, found := charset[i]
if !found {
return fmt.Errorf("invalid character: %v", i)
// isUpperASCII checks if string only contains ASCII alphanumeric upper case characters
func (v *validator) isUpperASCII(s string) error {
for _, r := range s {
if (r == 0x20) || (0x30 <= r && r <= 0x39) || 0x41 <= r && r <= 0x5A { // Space, 0 to 9, A to Z
continue
}
return fmt.Errorf("%w: %c", ErrUpperAlpha, r)
}
return nil
}

// isUpperAlphanumeric checks if string only contains ASCII alphanumeric upper case characters
func (v *validator) isUpperAlphanumeric(s string) error {
err := v.includesValidCharacters(s, validUppercaseAlphaNumericCharacters)
if err != nil {
return fmt.Errorf("%w: %v", ErrUpperAlpha, err)
}
return nil
}
var (
slashZero = []rune(`Ø`)[0]
)

// isAlphanumeric checks if a string only contains ASCII alphanumeric characters
func (v *validator) isAlphanumeric(s string) error {
err := v.includesValidCharacters(s, validAlphaNumericCharacters)
if err != nil {
return fmt.Errorf("%w: %v", ErrNonAlphanumeric, err)
for _, r := range s {
if 0x20 <= r && r <= 0x7E { // Space to ~ (Typical ASCII)
continue
}
if 0xC0 <= r && r <= 0xFF { // À to ÿ (Extended Latin Alphabet)
continue
}
// Specific characters that are accepted
switch r {
case
0xA2, // ¢ - Cent Sign
0xAC, // ¬ - Negation
0xA6, // ¦ - Pipe
0xB1, // ± - Plus or Minus Sign
slashZero:
continue

// case `¢`, `¬`, `¦`, `±`, `Ø`:
}
return fmt.Errorf("%w: %c", ErrNonAlphanumeric, r)
}
return nil
}
Expand Down
14 changes: 7 additions & 7 deletions validators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,15 @@ func TestValidators__isAlphanumeric(t *testing.T) {
name: "is alphanumeric",
checkFunc: v.isAlphanumeric,
shouldErr: func(i int) bool {
return i <= 0x1F || i > 0x7E
return (i <= 0x1F || i > 0x7E) && (i < 0xC0 || i > 0xFF)
},
},
// Ensure that ASCII characters from 0x20 to 0x60 and 0x7B to 0x7E are considered upper case alphanumeric.
// Ensure that ASCII characters from 0x41 to 0x5A are considered upper case ASCII.
{
name: "is upper alphanumeric",
checkFunc: v.isUpperAlphanumeric,
name: "is upper ascii",
checkFunc: v.isUpperASCII,
shouldErr: func(i int) bool {
return i <= 0x1F || i > 0x7E || (i > 0x60 && i < 0x7B)
return i != 0x20 && (i < 0x30 || i > 0x39) && (i < 0x41 || i > 0x5A)
},
},
}
Expand Down Expand Up @@ -187,13 +187,13 @@ func TestValidators__isAlphanumeric(t *testing.T) {
func TestValidators__isAlphanumericExamples(t *testing.T) {
v := validator{}

validCases := []string{"Acme Corp!", `|`, `¦`, `¢`, `¬`, `±`}
validCases := []string{"Acme Corp!", `|`, `¦`, `¢`, `¬`, `±`, `ã`, `è`, `ñ`}
for i := range validCases {
err := v.isAlphanumeric(validCases[i])
require.NoError(t, err, fmt.Sprintf("input: %q", validCases[i]))
}

invalidCases := []string{`©`, `®`, `§101.1`, `ã`, `è`}
invalidCases := []string{`©`, `®`, `§101.1`}
for i := range invalidCases {
err := v.isAlphanumeric(invalidCases[i])
require.ErrorIs(t, err, ErrNonAlphanumeric, fmt.Sprintf("input: %q", invalidCases[i]))
Expand Down

0 comments on commit c39e176

Please sign in to comment.