moov-io / iso8583

A golang implementation to marshal and unmarshal iso8583 message.
https://moov.io
Apache License 2.0
353 stars 105 forks source link

Filter fileds when display content of the message with `iso8583.Describe` #102

Closed alovak closed 1 year ago

alovak commented 3 years ago

We may create an option that will enable masking of the card number when we dump messages. That may be useful for logging, etc.

krishishah commented 3 years ago

Could we pass a flag / config value to toggle whether to mask such fields or not?

I imagine users of this lib may want to print out this value for testing / debugging

alovak commented 3 years ago

Yeah, I see this as an option.

wadearnold commented 3 years ago

I would hope the same flag could be passed back into #103 so that those errors which will be passed out of the library could be masked by default as well. By default, PII data wouldn't be returned unless the library was in verbose or debug mode.

Maybe we need a list of fields that we want to mask?

Field 02: PAN (Primary account number) Field 20: Primary account number extended Field 35: Track 2 data Field 36: Track 3 data Field 45: Track 1 data Field 52: PIN (Personal Identification Number) Field 55: EMV TLV data (this is a nested tree and should have its own config)

alovak commented 3 years ago

I think because of the different meanings of the fields in different specs, we can't rely on the field number.

Maybe, as idea, we can add Mask field into the Spec:

        2: field.NewString(&field.Spec{
            Length:      19,
            Description: "Primary Account Number",
            Enc:         encoding.ASCII,
            Pref:        prefix.ASCII.LL,
                        Mask:        macking.PAN,
        }),
package macking

type Masker func(string) string

var PAN Masker = func(number string) string {
  if number == "" || len(number) < 5  {
    return ""
  }

  in := []byte(number)
  return fmt.Sprintf("%s%s%s", in[:1], strings.Repeat("*", 6), in[len(in)-4:])
}

My assumption, that then we can mask data in errors, logs, describe.Message using provided Mask.

P.S. We can use something like https://pkg.go.dev/github.com/ggwhite/go-masker to avoid our own implementations of different masking functions.

alovak commented 1 year ago

Adding some more ideas so we can resume work on filtering data when we dump messages...

I'm looking at it from two perspectives:

  1. as a user of our Go package
  2. as a user of the CLI

1. For the Go package user

Currently, we have Describe method:

iso8583.Describe(os.Stdout, msg)

we can make Describe to be a variadic function that accepts optional filters for the fields:

iso8583.Describe(os.Stdout, msg, FilterField("2", PANFilter), FilterField("59", WhateverFilter))

we can also define DefaultFilters for fields we know 100%, like field 2, etc. so the user can:

iso8583.Describe(os.Stdout, msg, iso8583.DefaultFiledFilters)

I have a working POC here for Describe method: https://go.dev/play/p/0Efyo7kjjqe

2. For the CLI user

Currently, we have the describe command for which we can add filter argument(s) like this:

./iso8583 describe --filter 2:pan --filter 59:pan mgs.dat

where pan is defined filter in the package.

Also, it would be good to filter fields of the composite fields as well (in my POC, it works with int IDs, not strings)

adamdecaf commented 1 year ago

This looks pretty useful and there's prior art in the ACH package. We have fixed field positions so we can avoid having to specify the field # to mask. The decision to mask a field is passed through an Opts struct.

https://pkg.go.dev/github.com/moov-io/ach/cmd/achcli/describe

alovak commented 1 year ago

btw, @adamdecaf what do you think about:

instead of:

iso8583.Describe(os.Stdout, msg, iso8583.DefaultFieldFilters)

apply them by default? We may assume that, in most cases, the user will be safe displaying the message fields.

if the user does not want any filters, he can:

iso8583.Describe(os.Stdout, msg, iso8583.DoNotFilterFields)
adamdecaf commented 1 year ago

Yea, it's pretty common to use an int and masking to pass along several flags. The ...args option works as well and is easier to understand. The default of Describe can be to mask (as that's safer and is only for human readable use).

https://github.com/golang/go/blob/go1.19.3/src/text/tabwriter/tabwriter.go#L168-L208

I like having a default set of masking flags, especially if the card brands use the same fields we could mask by default.

The idx:type mapping of fields makes sense to me and can easily have a Go api.