Skip to content

Commit

Permalink
Add hashing feature & increase codecov (#19)
Browse files Browse the repository at this point in the history
* Add hashing feature & increase codecov

* Fix version command
  • Loading branch information
ahelmy committed Jan 7, 2024
1 parent 0436df0 commit 5df3f98
Show file tree
Hide file tree
Showing 14 changed files with 416 additions and 18 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,14 @@ xdev -h
Developer Swiss Army Knife tools X for anything.

Usage:
xdev [flags]
xdev [command]

Available Commands:
base64decoder Decode base64 string. Alias: b64d
base64decoder Decode base64 string. Alias: b64d
base64encoder Encode string to base64. Alias: b64e
completion Generate the autocompletion script for the specified shell
hash Hash a string
help Help about any command
json JSON indentation and minification
json2yaml Convert JSON to YAML. Alias: j2y
Expand All @@ -116,7 +118,8 @@ base64decoder Decode base64 string. Alias: b64d
yaml2json Convert YAML to JSON. Alias: y2j

Flags:
-h, --help help for xdev
-h, --help help for xdev
-v, --version Print the version number

Use "xdev [command] --help" for more information about a command.
```
Expand Down
66 changes: 66 additions & 0 deletions cmd/hash.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
*/
package cmd

import (
"fmt"
"strings"

"github.com/ahelmy/xdev/internal"
"github.com/spf13/cobra"
)

var validAlgorithms = []string{"md5", "sha256", "sha512", "salt"}

func validateAlgorithm(algorithm string) bool {
for _, a := range validAlgorithms {
if a == algorithm {
return true
}
}
return false
}

// hashCmd represents the hash command
var hashCmd = &cobra.Command{
Use: "hash",
Short: "Hash a string",
Long: `Hash a string. For example:
hash -a md5 "abc"`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) < 1 {
fmt.Println("Please provide an algorithm and a string to hash")
return
}
algorithm, err := cmd.Flags().GetString("algorithm")
if err != nil {
algorithm = "md5"
}
if !validateAlgorithm(algorithm) {
fmt.Println("Please provide a valid algorithm, valid algorithms are: " + strings.Join(validAlgorithms, ", "))
return
}
result, err := internal.HashString(args[0], algorithm)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(result)
},
}

func init() {
rootCmd.AddCommand(hashCmd)
hashCmd.Flags().StringP("algorithm", "a", "md5", "Hash algorithm")

// Here you will define your flags and configuration settings.

// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// hashCmd.PersistentFlags().String("foo", "", "A help for foo")

// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// hashCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
3 changes: 2 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ var rootCmd = &cobra.Command{
if versionFlag, _ := cmd.Flags().GetBool("version"); versionFlag {
fmt.Printf("Your App Version: %s\nCommit: %s\nBuild Date: %s\n", app.AppVersion, app.GitCommit, app.BuildTime)
os.Exit(0)
} else {
cmd.Help()
}
},
}
Expand All @@ -38,7 +40,6 @@ func Execute() {

func init() {
rootCmd.Flags().BoolP("version", "v", false, "Print the version number")
rootCmd.MarkFlagRequired("version")
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
Expand Down
2 changes: 1 addition & 1 deletion cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Copyright © 2024 NAME HERE <EMAIL ADDRESS>
package cmd

import (
"github.com/ahelmy/xdev/server"
server "github.com/ahelmy/xdev/server"
"github.com/spf13/cobra"
)

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ require (
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.51.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/sys v0.16.0 // indirect
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1S
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
Expand Down
40 changes: 40 additions & 0 deletions internal/hash.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package internal

import (
"crypto"
"encoding/hex"
"fmt"

"golang.org/x/crypto/bcrypt"
)

func HashString(str string, algorithm string) (string, error) {
switch algorithm {
case "md5":
return hash(str, crypto.MD5)
case "sha256":
return hash(str, crypto.SHA256)
case "sha512":
return hash(str, crypto.SHA512)
case "salt":
return salt(str)
}
return str, fmt.Errorf("invalid algorithm")
}

func hash(str string, algorithm crypto.Hash) (string, error) {
hasher := algorithm.New()

hasher.Write([]byte(str))

hashBytes := hasher.Sum(nil)

return hex.EncodeToString(hashBytes), nil
}
func salt(str string) (string, error) {
salt, err := bcrypt.GenerateFromPassword([]byte(str), 10)
if err != nil {
return "", err
}
return string(salt), nil
}
81 changes: 81 additions & 0 deletions internal/hash_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package internal

import (
"crypto/md5"
"crypto/sha256"
"crypto/sha512"
"fmt"
"testing"
)

func TestHashString(t *testing.T) {
testCases := []struct {
str string
algorithm string
expected string
}{
{
str: "Hello, World!",
algorithm: "md5",
expected: fmt.Sprintf("%x", md5.Sum([]byte("Hello, World!"))),
},
{
str: "Hello, World!",
algorithm: "sha256",
expected: fmt.Sprintf("%x", sha256.Sum256([]byte("Hello, World!"))),
},
{
str: "Hello, World!",
algorithm: "sha512",
expected: fmt.Sprintf("%x", sha512.Sum512([]byte("Hello, World!"))),
},

{
str: "Hello, World!",
algorithm: "invalid",
expected: "invalid algorithm",
},
}

for _, tc := range testCases {
result, err := HashString(tc.str, tc.algorithm)
if err != nil {
if err.Error() != tc.expected {
t.Errorf("Expected error: %s, but got: %s", tc.expected, err.Error())
}
} else {
if result != tc.expected {
t.Errorf("Expected result: %s, but got: %s", tc.expected, result)
}
}
}
}

func TestSalt(t *testing.T) {
testCases := []struct {
str string
length int
isError bool
}{
{
str: "Hello, World!",
length: 60,
isError: false,
},
{
str: "$2a$10$2dIj/VAy0Zhgy6eaNYjfAubIbyP5z2V7e8Qzhyr/xeo56GtQ22kOG$2a$10$2dIj/VAy0Zhgy6eaNYjfAubIbyP5z2V7e8Qzhyr/xeo56GtQ22kOG$2a$10$2dIj/VAy0Zhgy6eaNYjfAubIbyP5z2V7e8Qzhyr/xeo56GtQ22kOG$2a$10$2dIj/VAy0Zhgy6eaNYjfAubIbyP5z2V7e8Qzhyr/xeo56GtQ22kOG$2a$10$2dIj/VAy0Zhgy6eaNYjfAubIbyP5z2V7e8Qzhyr/xeo56GtQ22kOG$2a$10$2dIj/VAy0Zhgy6eaNYjfAubIbyP5z2V7e8Qzhyr/xeo56GtQ22kOG$2a$10$2dIj/VAy0Zhgy6eaNYjfAubIbyP5z2V7e8Qzhyr/xeo56GtQ22kOG$2a$10$2dIj/VAy0Zhgy6eaNYjfAubIbyP5z2V7e8Qzhyr/xeo56GtQ22kOG",
isError: true,
},
}

for _, tc := range testCases {
result, err := HashString(tc.str, "salt")
if err != nil && !tc.isError {
t.Errorf("Unexpected error: %v", err)
} else {
if len(result) != tc.length {
t.Errorf("Expected result: %v, but got: %v", tc.length, len(result))
}
}
}
}
34 changes: 29 additions & 5 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const (
PasswordPath = "/password"
Base64Path = "/base64"
URLPath = "/url"
HashPath = "/hash"
)

//go:embed ui/*
Expand Down Expand Up @@ -74,6 +75,7 @@ func StartServer(port int32, isVerbose bool) {
jwtPage(app)
base64Page(app)
urlPage(app)
hashPage(app)

log.Fatal(app.Listen(":"+strconv.FormatInt(int64(port), 10), fiber.ListenConfig{EnablePrefork: true}))
}
Expand Down Expand Up @@ -182,11 +184,7 @@ func yamlPage(app *fiber.App) {
yaml := c.FormValue("yaml")
result := ""
if len(yaml) > 0 {
if action == "beautify" {
errorStr = "Not implemented yet"
} else if action == "minify" {
errorStr = "Not implemented yet"
} else if action == "yaml2JSON" {
if action == "yaml2JSON" {
_yaml, err := internal.Yaml2Json(yaml)
if err != nil {
errorStr = err.Error()
Expand Down Expand Up @@ -289,6 +287,32 @@ func urlPage(app *fiber.App) {
})
}

func hashPage(app *fiber.App) {
app.Get(HashPath, func(c fiber.Ctx) error {
algorithm := c.FormValue("action")

str := c.FormValue("string")
result := ""
errorStr := ""
if len(str) > 0 {
_result, err := internal.HashString(str, algorithm)
if err != nil {
errorStr = err.Error()
} else {
result = _result
}
}

// Render index within layouts/main
return c.Render(Prefix+"hash", newMap(map[string]any{
"Title": "Hashing",
"String": str,
"Result": result,
"Error": errorStr,
}), MainLayout)
})
}

func defineResources(app *fiber.App) {
app.Use("/css", filesystem.New(filesystem.Config{
Root: fs.FS(cssFS),
Expand Down
Loading

0 comments on commit 5df3f98

Please sign in to comment.