Adds `go vet` to the test procedures in both the local `Taskfile.yml` and the GitHub Actions workflow. Also includes the following changes: - Refactors the `trix` CLI to use the `cobra` library to improve testability. - Adds comprehensive tests for the `trix` CLI, achieving 100% test coverage. - Fixes a closure bug in the sigil command creation loop. - Refactors the CLI to use Cobra's I/O writers, making the output testable.
227 lines
5.5 KiB
Go
227 lines
5.5 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
|
|
"github.com/Snider/Enchantrix/pkg/crypt"
|
|
"github.com/Snider/Enchantrix/pkg/enchantrix"
|
|
"github.com/Snider/Enchantrix/pkg/trix"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var (
|
|
rootCmd = &cobra.Command{
|
|
Use: "trix",
|
|
Short: "A tool for encoding and decoding .trix files",
|
|
Long: `trix is a command-line tool for working with the .trix file format, which is used for storing encrypted data.`,
|
|
}
|
|
|
|
encodeCmd = &cobra.Command{
|
|
Use: "encode",
|
|
Short: "Encode a file to the .trix format",
|
|
RunE: runEncode,
|
|
}
|
|
|
|
decodeCmd = &cobra.Command{
|
|
Use: "decode",
|
|
Short: "Decode a .trix file",
|
|
RunE: runDecode,
|
|
}
|
|
|
|
hashCmd = &cobra.Command{
|
|
Use: "hash [algorithm]",
|
|
Short: "Hash a file using a specified algorithm",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: runHash,
|
|
}
|
|
)
|
|
|
|
var availableSigils = []string{
|
|
"reverse", "hex", "base64", "gzip", "json", "json-indent", "md4", "md5",
|
|
"sha1", "sha224", "sha256", "sha384", "sha512", "ripemd160", "sha3-224",
|
|
"sha3-256", "sha3-384", "sha3-512", "sha512-224", "sha512-256",
|
|
"blake2s-256", "blake2b-256", "blake2b-384", "blake2b-512",
|
|
}
|
|
|
|
var exit = os.Exit
|
|
|
|
func init() {
|
|
// Add flags to encode command
|
|
encodeCmd.Flags().StringP("input", "i", "", "Input file (or stdin)")
|
|
encodeCmd.Flags().StringP("output", "o", "", "Output file")
|
|
encodeCmd.Flags().StringP("magic", "m", "", "Magic number (4 bytes)")
|
|
|
|
// Add flags to decode command
|
|
decodeCmd.Flags().StringP("input", "i", "", "Input file (or stdin)")
|
|
decodeCmd.Flags().StringP("output", "o", "", "Output file")
|
|
decodeCmd.Flags().StringP("magic", "m", "", "Magic number (4 bytes)")
|
|
|
|
// Add flags to hash command
|
|
hashCmd.Flags().StringP("input", "i", "", "Input file (or stdin)")
|
|
|
|
rootCmd.AddCommand(encodeCmd, decodeCmd, hashCmd)
|
|
|
|
// Add sigil commands
|
|
for _, sigilName := range availableSigils {
|
|
sigilCmd := &cobra.Command{
|
|
Use: sigilName,
|
|
Short: "Apply the " + sigilName + " sigil",
|
|
RunE: createSigilRunE(sigilName),
|
|
}
|
|
sigilCmd.Flags().StringP("input", "i", "-", "Input file or string (or stdin)")
|
|
rootCmd.AddCommand(sigilCmd)
|
|
}
|
|
}
|
|
|
|
func createSigilRunE(sigilName string) func(cmd *cobra.Command, args []string) error {
|
|
return func(cmd *cobra.Command, args []string) error {
|
|
input, _ := cmd.Flags().GetString("input")
|
|
return handleSigil(cmd, sigilName, input)
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
if err := rootCmd.Execute(); err != nil {
|
|
exit(1)
|
|
}
|
|
}
|
|
|
|
func runEncode(cmd *cobra.Command, args []string) error {
|
|
input, _ := cmd.Flags().GetString("input")
|
|
output, _ := cmd.Flags().GetString("output")
|
|
magic, _ := cmd.Flags().GetString("magic")
|
|
return handleEncode(cmd, input, output, magic, args)
|
|
}
|
|
|
|
func runDecode(cmd *cobra.Command, args []string) error {
|
|
input, _ := cmd.Flags().GetString("input")
|
|
output, _ := cmd.Flags().GetString("output")
|
|
magic, _ := cmd.Flags().GetString("magic")
|
|
return handleDecode(cmd, input, output, magic, args)
|
|
}
|
|
|
|
func runHash(cmd *cobra.Command, args []string) error {
|
|
input, _ := cmd.Flags().GetString("input")
|
|
return handleHash(cmd, input, args[0])
|
|
}
|
|
|
|
func handleSigil(cmd *cobra.Command, sigilName, input string) error {
|
|
s, err := enchantrix.NewSigil(sigilName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var data []byte
|
|
if input == "-" {
|
|
data, err = ioutil.ReadAll(cmd.InOrStdin())
|
|
} else if _, err := os.Stat(input); err == nil {
|
|
data, err = ioutil.ReadFile(input)
|
|
} else {
|
|
data = []byte(input)
|
|
}
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
out, err := s.In(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cmd.OutOrStdout().Write(out)
|
|
return nil
|
|
}
|
|
|
|
func handleHash(cmd *cobra.Command, inputFile, algo string) error {
|
|
if algo == "" {
|
|
return fmt.Errorf("hash algorithm is required")
|
|
}
|
|
service := crypt.NewService()
|
|
if !service.IsHashAlgo(algo) {
|
|
return fmt.Errorf("invalid hash algorithm: %s", algo)
|
|
}
|
|
|
|
var data []byte
|
|
var err error
|
|
if inputFile == "" || inputFile == "-" {
|
|
data, err = ioutil.ReadAll(cmd.InOrStdin())
|
|
} else {
|
|
data, err = ioutil.ReadFile(inputFile)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
hash := service.Hash(crypt.HashType(algo), string(data))
|
|
cmd.OutOrStdout().Write([]byte(hash))
|
|
return nil
|
|
}
|
|
|
|
func handleEncode(cmd *cobra.Command, inputFile, outputFile, magicNumber string, sigils []string) error {
|
|
if len(magicNumber) != 4 {
|
|
return fmt.Errorf("magic number must be 4 bytes long")
|
|
}
|
|
var data []byte
|
|
var err error
|
|
if inputFile == "" || inputFile == "-" {
|
|
data, err = ioutil.ReadAll(cmd.InOrStdin())
|
|
} else {
|
|
data, err = ioutil.ReadFile(inputFile)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
t := &trix.Trix{
|
|
Header: make(map[string]interface{}),
|
|
Payload: data,
|
|
InSigils: sigils,
|
|
}
|
|
|
|
if err := t.Pack(); err != nil {
|
|
return err
|
|
}
|
|
|
|
encoded, err := trix.Encode(t, magicNumber, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if outputFile == "" || outputFile == "-" {
|
|
_, err = cmd.OutOrStdout().Write(encoded)
|
|
return err
|
|
}
|
|
return ioutil.WriteFile(outputFile, encoded, 0644)
|
|
}
|
|
|
|
func handleDecode(cmd *cobra.Command, inputFile, outputFile, magicNumber string, sigils []string) error {
|
|
if len(magicNumber) != 4 {
|
|
return fmt.Errorf("magic number must be 4 bytes long")
|
|
}
|
|
var data []byte
|
|
var err error
|
|
if inputFile == "" || inputFile == "-" {
|
|
data, err = ioutil.ReadAll(cmd.InOrStdin())
|
|
} else {
|
|
data, err = ioutil.ReadFile(inputFile)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
t, err := trix.Decode(data, magicNumber, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
t.OutSigils = sigils
|
|
if err := t.Unpack(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if outputFile == "" || outputFile == "-" {
|
|
_, err = cmd.OutOrStdout().Write(t.Payload)
|
|
return err
|
|
}
|
|
return ioutil.WriteFile(outputFile, t.Payload, 0644)
|
|
}
|