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
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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 📡
----------