From 17fdd4483b7d736bfe2abe61ea5e864599e2f899 Mon Sep 17 00:00:00 2001 From: edoardottt Date: Sun, 5 Nov 2023 18:11:05 +0100 Subject: [PATCH 1/5] init --- .golangci.yml | 52 ++++++++++ README.md | 76 +++++++++++++- defan.go | 87 ++++++++++++++++ defan_test.go | 277 ++++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 11 ++ go.sum | 10 ++ 6 files changed, 511 insertions(+), 2 deletions(-) create mode 100644 .golangci.yml create mode 100644 defan.go create mode 100644 defan_test.go create mode 100644 go.mod create mode 100644 go.sum diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..5ef1c1e --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,52 @@ +linters: + # Enable all available linters. + # Default: false + disable-all: true + enable: + - asciicheck + - bodyclose + - dogsled + - dupl + - errcheck + - exhaustive + - exportloopref + - gochecknoglobals + - gochecknoinits + - goconst + - gocritic + - godot + - godox + - goerr113 + - goheader + - gomnd + - gomodguard + - goprintffuncname + - gosimple + - govet + - ineffassign + - lll + - misspell + - nakedret + - nolintlint + - prealloc + - rowserrcheck + - sqlclosecheck + - staticcheck + - stylecheck + - testpackage + - typecheck + - unconvert + - unparam + - unused + - whitespace + - wsl + +linters-settings: + wsl: + strict-append: false + enforce-err-cuddling: true + +issues: + exclude-rules: + - path: defan.go + text: "maybe you wanted to do Index" \ No newline at end of file diff --git a/README.md b/README.md index a51aab7..303d217 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,74 @@ -# defango -URL / IP / Email defanging with Golang. Make IoC harmless. +

+ defango +
+

+ +

URL / IP / Email defanging with Golang. Make IoC harmless.

+ +
Coded with 💙 by edoardottt
+ +

+ + + go action + + + + go report card + + +
+ + Share on Twitter! + +

+ +

+ Install • + Get Started • + Changelog • + Contributing • + License +

+ +

+ +

+ +Install 📡 +---------- + +```console +go install github.com/edoardottt/defango@latest +``` + +Get Started 🎉 +---------- + +```go +... +``` + +Changelog 📌 +------- + +Detailed changes for each release are documented in the [release notes](https://github.com/edoardottt/defango/releases). + +Contributing 🛠 +------- + +Just open an [issue](https://github.com/edoardottt/defango/issues) / [pull request](https://github.com/edoardottt/defango/pulls). + +Before opening a pull request, download [golangci-lint](https://golangci-lint.run/usage/install/) and run + +```bash +golangci-lint run +``` + +If there aren't errors, go ahead :) + +License 📝 +------- + +This repository is under [MIT License](https://github.com/edoardottt/defango/blob/main/LICENSE). +[edoardoottavianelli.it](https://www.edoardoottavianelli.it) to contact me. diff --git a/defan.go b/defan.go new file mode 100644 index 0000000..42fc156 --- /dev/null +++ b/defan.go @@ -0,0 +1,87 @@ +/* +defango - URL / IP / Email defanging with Golang. Make IoC harmless. + +This repository is under MIT License https://github.com/edoardottt/defango/blob/main/LICENSE +*/ + +package defango + +import ( + "errors" + "net/url" + "strings" +) + +var ( + ErrUnsupportedInputType = errors.New("unsupported input type") +) + +// IP returns a defanged IP. +// Support both IPv4 and IPv6. +func IP(input string) string { + result := "" + + if strings.Contains(input, "::") { // IPv6 with double column + index := strings.Index(input, "::") + result = strings.ReplaceAll(input[:index], ":", "[:]") + + "[::]" + + strings.ReplaceAll(input[index+2:], ":", "[:]") + } else { // Full IPv6 or IPv4 + result = strings.ReplaceAll(strings.ReplaceAll(input, ".", "[.]"), ":", "[:]") + } + + return result +} + +// URL returns a defanged URL. +// Accept string and url.URL. +// For security reasons, if the url is not formatted in a proper way +// the result is an empty string with a non-nil error. +func URL(input interface{}) (string, error) { + switch v := input.(type) { + case string: + return defangURL(v) + case url.URL: + return defangURL(v.String()) + default: + return "", ErrUnsupportedInputType + } +} + +func defangURL(input string) (string, error) { + result := "" + + if strings.Contains(input, "://") { // handle protocol + result += defangProtocols(input) + "://" + result += strings.ReplaceAll( + strings.ReplaceAll( + input[strings.Index(input, "://")+3:], ".", "[.]"), + ":", "[:]") + } else { + result += strings.ReplaceAll(strings.ReplaceAll(input, ".", "[.]"), ":", "[:]") + } + + return result, nil +} + +func defangProtocols(input string) string { + protoMap := map[string]string{ + "http": "hxxp", + "https": "hxxps", + "ftp": "fxp", + "file": "fxle", + } + proto := input[:strings.Index(input, "://")] + + protoDefanged, ok := protoMap[proto] + if ok { + return protoDefanged + } else { + return proto + } +} + +// Email returns a defanged email address/link. +func Email(input string) string { + return strings.ReplaceAll(strings.ReplaceAll(input, ".", "[.]"), ":", "[:]") +} diff --git a/defan_test.go b/defan_test.go new file mode 100644 index 0000000..f9c2816 --- /dev/null +++ b/defan_test.go @@ -0,0 +1,277 @@ +/* +defango - URL / IP / Email defanging with Golang. Make IoC harmless. + +This repository is under MIT License https://github.com/edoardottt/defango/blob/main/LICENSE +*/ + +package defango_test + +import ( + "net/url" + "testing" + + "github.com/edoardottt/defango" + "github.com/stretchr/testify/require" +) + +func TestIP(t *testing.T) { + tests := []struct { + name string + input string + want string + }{ + { + name: "empty string", + input: "", + want: "", + }, + { + name: "Ipv4", + input: "8.8.8.8", + want: "8[.]8[.]8[.]8", + }, + { + name: "Short Ipv4", + input: "1.1", + want: "1[.]1", + }, + { + name: "Ipv4 + port", + input: "8.8.8.8:53", + want: "8[.]8[.]8[.]8[:]53", + }, + { + name: "Short Ipv4 + port", + input: "8.8:53", + want: "8[.]8[:]53", + }, + { + name: "Ipv6", + input: "2001:0db8:85a3:0000:0000:8a2e:0370:7334", + want: "2001[:]0db8[:]85a3[:]0000[:]0000[:]8a2e[:]0370[:]7334", + }, + { + name: "Short Ipv6", + input: "2001:0db8:85a3::8a2e:0370:7334", + want: "2001[:]0db8[:]85a3[::]8a2e[:]0370[:]7334", + }, + { + name: "Ipv6 + port", + input: "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8080", + want: "[2001[:]0db8[:]85a3[:]0000[:]0000[:]8a2e[:]0370[:]7334][:]8080", + }, + { + name: "Short Ipv6 + port", + input: "[2001:0db8:85a3::8a2e:0370:7334]:8080", + want: "[2001[:]0db8[:]85a3[::]8a2e[:]0370[:]7334][:]8080", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := defango.IP(tt.input) + require.Equal(t, tt.want, got) + }) + } +} + +func TestURL(t *testing.T) { + tests := []struct { + name string + input interface{} + want string + err error + }{ + { + name: "empty string", + input: "", + want: "", + err: nil, + }, + { + name: "https URL", + input: "https://www.edoardoottavianelli.it", + want: "hxxps://www[.]edoardoottavianelli[.]it", + err: nil, + }, + { + name: "https URL with path", + input: "https://github.com/edoardottt/defangjs", + want: "hxxps://github[.]com/edoardottt/defangjs", + err: nil, + }, + { + name: "http URL", + input: "http://www.edoardoottavianelli.it", + want: "hxxp://www[.]edoardoottavianelli[.]it", + err: nil, + }, + { + name: "http URL with path", + input: "http://github.com/edoardottt/defangjs", + want: "hxxp://github[.]com/edoardottt/defangjs", + err: nil, + }, + { + name: "http URL and port", + input: "http://www.edoardoottavianelli.it:8080", + want: "hxxp://www[.]edoardoottavianelli[.]it[:]8080", + err: nil, + }, + { + name: "http URL with path and port", + input: "http://github.com:8000/edoardottt/defangjs", + want: "hxxp://github[.]com[:]8000/edoardottt/defangjs", + err: nil, + }, + { + name: "ftp URL", + input: "ftp://github.com/edoardottt/defangjs", + want: "fxp://github[.]com/edoardottt/defangjs", + err: nil, + }, + { + name: "ftp URL and port", + input: "ftp://github.com:8000/edoardottt/defangjs", + want: "fxp://github[.]com[:]8000/edoardottt/defangjs", + err: nil, + }, + { + name: "file URL", + input: "file:///etc/hosts", + want: "fxle:///etc/hosts", + err: nil, + }, + { + name: "URL no protocol", + input: "www.edoardoottavianelli.it", + want: "www[.]edoardoottavianelli[.]it", + err: nil, + }, + { + name: "URL malformed protocol", + input: "://www.edoardoottavianelli.it", + want: "://www[.]edoardoottavianelli[.]it", + err: nil, + }, + { + name: "URL no protocol and port", + input: "www.edoardoottavianelli.it:8080", + want: "www[.]edoardoottavianelli[.]it[:]8080", + err: nil, + }, + { + name: "https URL (url.URL)", + input: *parseURL("https://www.edoardoottavianelli.it"), + want: "hxxps://www[.]edoardoottavianelli[.]it", + err: nil, + }, + { + name: "https URL with path (url.URL)", + input: *parseURL("https://github.com/edoardottt/defangjs"), + want: "hxxps://github[.]com/edoardottt/defangjs", + err: nil, + }, + { + name: "http URL (url.URL)", + input: *parseURL("http://www.edoardoottavianelli.it"), + want: "hxxp://www[.]edoardoottavianelli[.]it", + err: nil, + }, + { + name: "http URL with path (url.URL)", + input: *parseURL("http://github.com/edoardottt/defangjs"), + want: "hxxp://github[.]com/edoardottt/defangjs", + err: nil, + }, + { + name: "http URL and port (url.URL)", + input: *parseURL("http://www.edoardoottavianelli.it:8080"), + want: "hxxp://www[.]edoardoottavianelli[.]it[:]8080", + err: nil, + }, + { + name: "http URL with path and port (url.URL)", + input: *parseURL("http://github.com:8000/edoardottt/defangjs"), + want: "hxxp://github[.]com[:]8000/edoardottt/defangjs", + err: nil, + }, + { + name: "ftp URL (url.URL)", + input: *parseURL("ftp://github.com/edoardottt/defangjs"), + want: "fxp://github[.]com/edoardottt/defangjs", + err: nil, + }, + { + name: "ftp URL and port (url.URL)", + input: *parseURL("ftp://github.com:8000/edoardottt/defangjs"), + want: "fxp://github[.]com[:]8000/edoardottt/defangjs", + err: nil, + }, + { + name: "file URL (url.URL)", + input: *parseURL("file:///etc/hosts"), + want: "fxle:///etc/hosts", + err: nil, + }, + { + name: "URL no protocol (url.URL)", + input: *parseURL("www.edoardoottavianelli.it"), + want: "www[.]edoardoottavianelli[.]it", + err: nil, + }, + { + name: "URL no protocol and port (url.URL)", + input: *parseURL("www.edoardoottavianelli.it:8080"), + want: "www[.]edoardoottavianelli[.]it[:]8080", + err: nil, + }, + { + name: "float/integer input", + input: 2, + want: "", + err: defango.ErrUnsupportedInputType, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := defango.URL(tt.input) + require.Equal(t, tt.err, err) + require.Equal(t, tt.want, got) + }) + } +} + +func TestEmail(t *testing.T) { + tests := []struct { + name string + input string + want string + }{ + { + name: "empty string", + input: "", + want: "", + }, + { + name: "email address", + input: "edoardott@gmail.com", + want: "edoardott@gmail[.]com", + }, + { + name: "email link", + input: "mailto:edoardott@gmail.com", + want: "mailto[:]edoardott@gmail[.]com", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := defango.Email(tt.input) + require.Equal(t, tt.want, got) + }) + } +} + +func parseURL(input string) *url.URL { + u, _ := url.Parse(input) + return u +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1981e8c --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module github.com/edoardottt/defango + +go 1.21.2 + +require github.com/stretchr/testify v1.8.4 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..fa4b6e6 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 032a751e7db2477c87d2fe8d79066d08ba507c27 Mon Sep 17 00:00:00 2001 From: Edoardo Ottavianelli Date: Sun, 5 Nov 2023 18:11:53 +0100 Subject: [PATCH 2/5] Create go.yml --- .github/workflows/go.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/go.yml diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..01214ef --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,28 @@ +# This workflow will build a golang project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go + +name: Go + +on: + push: + branches: [ "main","devel" ] + pull_request: + branches: [ "main","devel" ] + +jobs: + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.21' + + - name: Build + run: go build -v ./... + + - name: Test + run: go test -v ./... From 95331344d17cbdc3ebe7eedd1e7d9e1ece51a0f8 Mon Sep 17 00:00:00 2001 From: Edoardo Ottavianelli Date: Sun, 5 Nov 2023 18:15:17 +0100 Subject: [PATCH 3/5] Create dependabot.yml --- .github/dependabot.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..8cd4ada --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,14 @@ +version: 2 +updates: + + # Maintain dependencies for go modules + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "weekly" + target-branch: "devel" + commit-message: + prefix: "chore" + include: "scope" + labels: + - "Maintenance" From f35b88d2da31e632c7f0a38e849ea631fee99ad1 Mon Sep 17 00:00:00 2001 From: edoardottt Date: Sun, 5 Nov 2023 18:16:28 +0100 Subject: [PATCH 4/5] init cicd --- .github/auto_assign.yml | 14 +++++++++ .github/workflows/golangci-lint.yml | 46 +++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 .github/auto_assign.yml create mode 100644 .github/workflows/golangci-lint.yml diff --git a/.github/auto_assign.yml b/.github/auto_assign.yml new file mode 100644 index 0000000..e3e74f7 --- /dev/null +++ b/.github/auto_assign.yml @@ -0,0 +1,14 @@ +# Set to true to add reviewers to pull requests +addReviewers: true + +# A list of reviewers to be added to pull requests (GitHub user name) +reviewers: + - edoardottt + +# A list of keywords to be skipped the process that add reviewers if pull requests include it +skipKeywords: + - wip + +# A number of reviewers added to the pull request +# Set 0 to add all the reviewers (default: 0) +numberOfReviewers: 0 \ No newline at end of file diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 0000000..a295c4c --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,46 @@ +name: golangci-lint +on: + push: + tags: + - v* + branches: + - devel + - main + pull_request: +permissions: + contents: read + # Optional: allow read access to pull request. Use with `only-new-issues` option. + # pull-requests: read +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/setup-go@v3 + with: + go-version: 1.21 + - uses: actions/checkout@v3 + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version + version: v1.55 + + # Optional: working directory, useful for monorepos + # working-directory: somedir + + # Optional: golangci-lint command line arguments. + # args: --issues-exit-code=0 + + # Optional: show only new issues if it's a pull request. The default value is `false`. + # only-new-issues: true + + # Optional: if set to true then the all caching functionality will be complete disabled, + # takes precedence over all other caching options. + # skip-cache: true + + # Optional: if set to true then the action don't cache or restore ~/go/pkg. + # skip-pkg-cache: true + + # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. + # skip-build-cache: true \ No newline at end of file From 791aaca78f7744c572f97f2213d8c9a5012cb4aa Mon Sep 17 00:00:00 2001 From: edoardottt Date: Sun, 5 Nov 2023 18:18:43 +0100 Subject: [PATCH 5/5] udpate readme --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 303d217..8f5930c 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@
- Share on Twitter! + Share on Twitter!

@@ -31,10 +31,6 @@ License

-

- -

- Install 📡 ----------