Skip to content

Commit

Permalink
Merge pull request #1262 from adamdecaf/vert-bar-validation
Browse files Browse the repository at this point in the history
fix: allow full range of ASCII and EBCDIC characters accepted by Nacha
  • Loading branch information
adamdecaf authored Jul 18, 2023
2 parents ea5a43e + 85b7e3c commit 9b612fd
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 11 deletions.
38 changes: 29 additions & 9 deletions validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@ import (
)

var (
upperAlphanumericRegex = regexp.MustCompile(fmt.Sprintf(
`[^ A-Z0-9!"#$%%&'()*+,-.\\/:;<>=?@\[\]^_{}|~%s]+`, "`"))
alphanumericRegex = regexp.MustCompile(fmt.Sprintf(
`[^ \w!"#$%%&'()*+,-.\\/:;<>=?@\[\]^_{}|~%s]+`, "`"))
lowerAlphaCharacters = "abcdefghijklmnopqrstuvwxyz"
numericCharacters = "0123456789"
asciiCharacters = ` !"#$%&'()*+,-./:;<=>?@[\]^_{|}~` + "`"
ebcdicExtraCharacters = `¢¬¦±`

validAlphaNumericCharacters = lowerAlphaCharacters + strings.ToUpper(lowerAlphaCharacters) + numericCharacters + asciiCharacters + ebcdicExtraCharacters
validUppercaseAlphaNumericCharacters = strings.ToUpper(lowerAlphaCharacters) + numericCharacters + asciiCharacters + ebcdicExtraCharacters
)

// validator is common validation and formatting of golang types to ach type strings
Expand Down Expand Up @@ -422,19 +425,36 @@ func (v *validator) isTransactionTypeCode(s string) error {
return ErrTransactionTypeCode
}

func (v *validator) includesValidCharacters(input string, charset string) error {
for _, i := range input {
var found bool
for _, c := range charset {
if i == c {
found = true
break
}
}
if !found {
return fmt.Errorf("invalid character: %v", i)
}
}
return nil
}

// isUpperAlphanumeric checks if string only contains ASCII alphanumeric upper case characters
func (v *validator) isUpperAlphanumeric(s string) error {
if upperAlphanumericRegex.MatchString(s) {
return ErrUpperAlpha
err := v.includesValidCharacters(s, validUppercaseAlphaNumericCharacters)
if err != nil {
return fmt.Errorf("%w: %v", ErrUpperAlpha, err)
}
return nil
}

// isAlphanumeric checks if a string only contains ASCII alphanumeric characters
func (v *validator) isAlphanumeric(s string) error {
if alphanumericRegex.MatchString(s) {
// ^[ A-Za-z0-9_@./#&+-]*$/
return ErrNonAlphanumeric
err := v.includesValidCharacters(s, validAlphaNumericCharacters)
if err != nil {
return fmt.Errorf("%w: %v", ErrNonAlphanumeric, err)
}
return nil
}
Expand Down
25 changes: 23 additions & 2 deletions validators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,15 +145,15 @@ func TestValidators__isAlphanumeric(t *testing.T) {
name: "is alphanumeric",
checkFunc: v.isAlphanumeric,
shouldErr: func(i int) bool {
return i < 0x20 || i > 0x7E
return i <= 0x1F || i > 0x7E
},
},
// Ensure that ASCII characters from 0x20 to 0x60 and 0x7B to 0x7E are considered upper case alphanumeric.
{
name: "is upper alphanumeric",
checkFunc: v.isUpperAlphanumeric,
shouldErr: func(i int) bool {
return i < 0x20 || i > 0x7E || (i > 0x60 && i < 0x7B)
return i <= 0x1F || i > 0x7E || (i > 0x60 && i < 0x7B)
},
},
}
Expand All @@ -165,6 +165,11 @@ func TestValidators__isAlphanumeric(t *testing.T) {
err := tt.checkFunc(chr)
shouldError := tt.shouldErr(i)

switch chr {
case `¢`, `¦`, `¬`, `±`: // skip valid ASCII characters
continue
}

if shouldError && err == nil {
t.Errorf("expected rune %x (%s) to be non-alphanumeric", i, chr)
} else if !shouldError && err != nil {
Expand All @@ -175,6 +180,22 @@ func TestValidators__isAlphanumeric(t *testing.T) {
}
}

func TestValidators__isAlphanumericExamples(t *testing.T) {
v := validator{}

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

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

func TestValidators__validateJulianDay(t *testing.T) {
empty := " "
cases := map[string]string{
Expand Down

0 comments on commit 9b612fd

Please sign in to comment.