Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Seccomp filter for whitelisting syscalls #82

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,16 @@
"syscall"
"time"
"unsafe"

Check failure on line 48 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L48

File is not `gofmt`-ed with `-s` (gofmt)

Check failure on line 48 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L48

File is not `gci`-ed with --skip-generated -s standard -s default (gci)
"github.com/hlandau/dexlogconfig"

Check failure on line 49 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L49

import 'github.com/hlandau/dexlogconfig' is not allowed from list 'Main' (depguard)

Check failure on line 49 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L49

import 'github.com/hlandau/dexlogconfig' is not allowed from list 'Main' (depguard)
"github.com/hlandau/xlog"

Check failure on line 50 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L50

import 'github.com/hlandau/xlog' is not allowed from list 'Main' (depguard)

Check failure on line 50 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L50

import 'github.com/hlandau/xlog' is not allowed from list 'Main' (depguard)
"github.com/oraoto/go-pidfd"

Check failure on line 51 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L51

import 'github.com/oraoto/go-pidfd' is not allowed from list 'Main' (depguard)

Check failure on line 51 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L51

import 'github.com/oraoto/go-pidfd' is not allowed from list 'Main' (depguard)
"github.com/robertmin1/heteronculous-horklump/httpproxy"

Check failure on line 52 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L52

import 'github.com/robertmin1/heteronculous-horklump/httpproxy' is not allowed from list 'Main' (depguard)

Check failure on line 52 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L52

import 'github.com/robertmin1/heteronculous-horklump/httpproxy' is not allowed from list 'Main' (depguard)
"github.com/robertmin1/socks5/v4"

Check failure on line 53 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L53

import 'github.com/robertmin1/socks5/v4' is not allowed from list 'Main' (depguard)

Check failure on line 53 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L53

import 'github.com/robertmin1/socks5/v4' is not allowed from list 'Main' (depguard)
"github.com/u-root/u-root/pkg/strace"

Check failure on line 54 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L54

import 'github.com/u-root/u-root/pkg/strace' is not allowed from list 'Main' (depguard)

Check failure on line 54 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L54

import 'github.com/u-root/u-root/pkg/strace' is not allowed from list 'Main' (depguard)
"golang.org/x/sys/unix"
"gopkg.in/hlandau/easyconfig.v1"
seccomp "github.com/elastic/go-seccomp-bpf"

Check failure on line 57 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L57

File is not `gci`-ed with --skip-generated -s standard -s default (gci)
)

var (
Expand All @@ -70,18 +71,18 @@
var exitAddr sync.Map

// Config is a struct to store the program's configuration values.
type Config struct { //nolint

Check failure on line 74 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L74

directive `//nolint` is unused (nolintlint)

Check failure on line 74 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L74

directive `//nolint` is unused (nolintlint)
Program string `usage:"Program Name"`
SocksTCP string `default:"127.0.0.1:9050"`
Args []string `usage:"Program Arguments"`
KillProg bool `default:"false" usage:"Kill the Program in case of a Proxy Leak (bool)"`

Check failure on line 78 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L78

tag is not aligned, should be: default:"false" usage:"Kill the Program in case of a Proxy Leak (bool)" (tagalign)

Check failure on line 78 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L78

tag is not aligned, should be: default:"false" usage:"Kill the Program in case of a Proxy Leak (bool)" (tagalign)
LogLeaks bool `default:"false" usage:"Allow Proxy Leaks but Log any that Occur (bool)"`

Check failure on line 79 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L79

tag is not aligned, should be: default:"false" usage:"Allow Proxy Leaks but Log any that Occur (bool)" (tagalign)

Check failure on line 79 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L79

tag is not aligned, should be: default:"false" usage:"Allow Proxy Leaks but Log any that Occur (bool)" (tagalign)
EnvVar bool `default:"true" usage:"Use the Environment Vars TOR_SOCKS_HOST and TOR_SOCKS_PORT (bool)"`

Check failure on line 80 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L80

tag is not aligned, should be: default:"true" usage:"Use the Environment Vars TOR_SOCKS_HOST and TOR_SOCKS_PORT (bool)" (tagalign)

Check failure on line 80 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L80

tag is not aligned, should be: default:"true" usage:"Use the Environment Vars TOR_SOCKS_HOST and TOR_SOCKS_PORT (bool)" (tagalign)
Redirect string `default:"socks5" usage:"Incase of leak redirect to the desired proxy(socks5,http,trans)"`

Check failure on line 81 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L81

tag is not aligned, should be: default:"socks5" usage:"Incase of leak redirect to the desired proxy(socks5,http,trans)" (tagalign)

Check failure on line 81 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L81

tag is not aligned, should be: default:"socks5" usage:"Incase of leak redirect to the desired proxy(socks5,http,trans)" (tagalign)
Proxyuser string `default:"" usage:"Proxy username in case of proxy redirection"`

Check failure on line 82 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L82

tag is not aligned, should be: default:"" usage:"Proxy username in case of proxy redirection" (tagalign)

Check failure on line 82 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L82

tag is not aligned, should be: default:"" usage:"Proxy username in case of proxy redirection" (tagalign)
Proxypass string `default:"" usage:"Proxy password in case of proxy redirection"`

Check failure on line 83 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L83

tag is not aligned, should be: default:"" usage:"Proxy password in case of proxy redirection" (tagalign)

Check failure on line 83 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L83

tag is not aligned, should be: default:"" usage:"Proxy password in case of proxy redirection" (tagalign)
OneCircuit bool `default:"false" usage:"Disable random SOCKS behavior"`

Check failure on line 84 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L84

tag is not aligned, should be: default:"false" usage:"Disable random SOCKS behavior" (tagalign)

Check failure on line 84 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L84

tag is not aligned, should be: default:"false" usage:"Disable random SOCKS behavior" (tagalign)
WhitelistLoopback bool `default:"false" usage:"Whitelist outgoing IP connections to loopback addresses (e.g. 127.0.0.1)"` //nolint:lll

Check failure on line 85 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L85

tag is not aligned, should be: default:"false" usage:"Whitelist outgoing IP connections to loopback addresses (e.g. 127.0.0.1)" (tagalign)

Check failure on line 85 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L85

tag is not aligned, should be: default:"false" usage:"Whitelist outgoing IP connections to loopback addresses (e.g. 127.0.0.1)" (tagalign)
}

// FullAddress is the network address and port
Expand All @@ -103,7 +104,7 @@
Port uint16
}

func main() {

Check failure on line 107 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L107

calculated cyclomatic complexity for function main is 15, max is 10 (cyclop)
// Create a Config and initialize it with default values.
cfg := Config{}
config := easyconfig.Configurator{
Expand Down Expand Up @@ -137,8 +138,14 @@
cfg.Proxypass = password
}

err := applySeccompFilter()
if err != nil {
fmt.Printf("Error applying seccomp filter: %v\n", err)

Check failure on line 143 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L143

use of `fmt.Printf` forbidden by pattern `^(fmt\.Print(|f|ln)|print|println)$` (forbidigo)

Check failure on line 143 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L143

use of `fmt.Printf` forbidden by pattern `^(fmt\.Print(|f|ln)|print|println)$` (forbidigo)
return

Check failure on line 144 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L144

return with no blank line before (nlreturn)

Check failure on line 144 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L144

return with no blank line before (nlreturn)
}

// Start the program with tracing and handle the CONNECT system call events.
if err := strace.Trace(program, func(t strace.Task, record *strace.TraceRecord) error {

Check failure on line 148 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L148

parameter name 't' is too short for the scope of its usage (varnamelen)
if record.Event == strace.SyscallEnter && record.Syscall.Sysno == unix.SYS_CONNECT {
if err := HandleConnect(t, record, program, cfg); err != nil {
return err
Expand Down Expand Up @@ -218,7 +225,7 @@
}

return nil
// TODO: handle invalid flag

Check failure on line 228 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L228

main.go:228: Line contains TODO/BUG/FIXME: "TODO: handle invalid flag" (godox)
// Incase trans proxy will require a different implementation a switch will be used.
}

Expand All @@ -233,7 +240,7 @@

// eventName returns an event name. There should never be an event name
// we do not known and, if we encounter one, we panic.
func eventName(r *strace.TraceRecord) (string, error) { //nolint

Check failure on line 243 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L243

directive `//nolint` is unused (nolintlint)
// form up a reasonable name for a system call.
// If there is no name, then it will be Exxxx or Xxxxx, where x
// is the system call number as %04x.
Expand All @@ -254,16 +261,16 @@
case strace.SyscallExit:
return sysName, nil
case strace.SignalExit:
return fmt.Sprintf("SignalExit"), nil

Check failure on line 264 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L264

fmt.Sprintf can be replaced with just using the string (perfsprint)

Check failure on line 264 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L264

S1039: unnecessary use of fmt.Sprintf (gosimple)
case strace.Exit:
return fmt.Sprintf("Exit"), nil

Check failure on line 266 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L266

fmt.Sprintf can be replaced with just using the string (perfsprint)

Check failure on line 266 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L266

S1039: unnecessary use of fmt.Sprintf (gosimple)
case strace.SignalStop:
return fmt.Sprintf("SignalStop"), nil

Check failure on line 268 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L268

fmt.Sprintf can be replaced with just using the string (perfsprint)

Check failure on line 268 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L268

S1039: unnecessary use of fmt.Sprintf (gosimple)
case strace.NewChild:
return fmt.Sprintf("NewChild"), nil
}

return "", fmt.Errorf("unknown event %#x from record %v", r.Event, r)

Check failure on line 273 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L273

do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"unknown event %#x from record %v\", r.Event, r)" (err113)
}

// ParseAddress reads an sockaddr struct from the given address and converts it
Expand Down Expand Up @@ -347,7 +354,7 @@
// out.NIC = NICID(a.Scope_id)
//}

if out.Addr == strings.Repeat(nullByte, 16) {

Check failure on line 357 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L357

Magic number: 16, in <argument> detected (mnd)

Check failure on line 357 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L357

Magic number: 16, in <argument> detected (gomnd)
out.Addr = ""
}

Expand Down Expand Up @@ -462,7 +469,7 @@
case parsedhost.To4()[0] == UDPProtolNum:
log.Error("Support for UDP will be implemented")
default:
return errors.New("invalid ip address")

Check failure on line 472 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L472

do not define dynamic errors, use wrapped static errors instead: "errors.New(\"invalid ip address\")" (err113)
}

// Poking our proxy IP/Port to the address containing the original address
Expand All @@ -475,7 +482,7 @@
return nil
}

func Socksify(args strace.SyscallArguments, record *strace.TraceRecord, t strace.Task, cfg Config) error {

Check failure on line 485 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L485

unused-parameter: parameter 'args' seems to be unused, consider removing or renaming it as _ (revive)
username, password := cfg.Proxyuser, cfg.Proxypass

if !cfg.OneCircuit {
Expand All @@ -491,7 +498,7 @@

addr, _ := exitAddr.LoadAndDelete(record.PID)
IPPort := fmt.Sprintf("%v", addr)
fd := record.Syscall.Args[0].Uint()

Check failure on line 501 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L501

variable name 'fd' is too short for the scope of its usage (varnamelen)

p, err := pidfd.Open(record.PID, 0)
if err != nil {
Expand All @@ -512,9 +519,9 @@

switch cfg.Redirect {
case "socks5":
cl, err := socks5.NewClient(IPPort, username, password, 10, 10)

Check failure on line 522 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L522

Magic number: 10, in <argument> detected (mnd)

Check failure on line 522 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L522

Magic number: 10, in <argument> detected (gomnd)
if err != nil {
return err

Check failure on line 524 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L524

error returned from external package is unwrapped: sig: func github.com/robertmin1/socks5/v4.NewClient(addr string, username string, password string, tcpTimeout int, udpTimeout int) (*github.com/robertmin1/socks5/v4.Client, error) (wrapcheck)
}

_, err = cl.Dial("tcp", IPPort, conn)
Expand All @@ -525,12 +532,12 @@
case "http":
cl, err := httpproxy.NewClient(cfg.SocksTCP, username, password)
if err != nil {
return err

Check failure on line 535 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L535

error returned from external package is unwrapped: sig: func github.com/robertmin1/heteronculous-horklump/httpproxy.NewClient(addr string, username string, password string) (*github.com/robertmin1/heteronculous-horklump/httpproxy.HTTPDialer, error) (wrapcheck)
}

_, err = cl.Dial("tcp", IPPort, conn)
if err != nil {
return err

Check failure on line 540 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L540

error returned from external package is unwrapped: sig: func (*github.com/robertmin1/heteronculous-horklump/httpproxy.HTTPDialer).Dial(network string, addr string, httpconn net.Conn) (net.Conn, error) (wrapcheck)
}
}

Expand All @@ -541,7 +548,7 @@
// Create or open the log file in append mode
logFile, err := os.OpenFile("stack_trace.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) //nolint
if err != nil {
return err

Check failure on line 551 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L551

error returned from external package is unwrapped: sig: func os.OpenFile(name string, flag int, perm io/fs.FileMode) (*os.File, error) (wrapcheck)
}

defer logFile.Close()
Expand All @@ -552,7 +559,7 @@

commBytes, err := os.ReadFile(commPath)
if err != nil {
return err

Check failure on line 562 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L562

error returned from external package is unwrapped: sig: func os.ReadFile(name string) ([]byte, error) (wrapcheck)
}
// Split the contents by null byte to separate command and arguments
cmdline := strings.Split(string(commBytes), "\x00")
Expand All @@ -566,7 +573,7 @@
go_log.Printf("Program and Arguments:%v\n", cmdline)

// Get the stack trace
stack := make([]byte, 8192)

Check failure on line 576 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L576

Magic number: 8192, in <argument> detected (mnd)

Check failure on line 576 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L576

Magic number: 8192, in <argument> detected (gomnd)
length := runtime.Stack(stack, true)

// Write the stack trace to the log file
Expand All @@ -589,16 +596,16 @@
}

func GenerateRandomCredentials() (string, error) {
bytes := make([]byte, 48)

Check failure on line 599 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L599

Magic number: 48, in <argument> detected (mnd)

Check failure on line 599 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L599

Magic number: 48, in <argument> detected (gomnd)
if _, err := rand.Read(bytes); err != nil {
return "", err

Check failure on line 601 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L601

error returned from external package is unwrapped: sig: func crypto/rand.Read(b []byte) (n int, err error) (wrapcheck)
}

return hex.EncodeToString(bytes), nil
}

func initializeAuthData() {
for i := 0; i < 10; i++ {

Check failure on line 608 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L608

for loop can be changed to use an integer range (Go 1.22+) (intrange)

Check failure on line 608 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L608

for loop can be changed to use an integer range (Go 1.22+) (intrange)
username, _ := GenerateRandomCredentials()
password, _ := GenerateRandomCredentials()
authData = append(authData, struct {
Expand All @@ -607,3 +614,60 @@
}{username, password})
}
}

func applySeccompFilter() error{

Check failure on line 618 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L618

File is not `gci`-ed with --skip-generated -s standard -s default (gci)
// Create a filter.
filter := seccomp.Filter{
NoNewPrivs: true,
Flag: seccomp.FilterFlagTSync,
Policy: seccomp.Policy{
DefaultAction: seccomp.ActionErrno,
Syscalls: []seccomp.SyscallGroup{
{
Action: seccomp.ActionAllow,
// Torsocks syscall whitelist https://gitlab.torproject.org/tpo/core/torsocks/-/blob/main/src/lib/syscall.c?ref_type=heads#L486

Check failure on line 628 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L628

the line is 132 characters long, which exceeds the maximum of 120 characters. (lll)
Names: []string{
"socket", // Create an endpoint for communication (network socket)
"connect", // Initiate a connection on a socket
"close", // Close a file descriptor, socket, or resource
"mmap", // Map files or devices into memory
"munmap", // Unmap previously mapped memory
"accept", // Accept a connection on a socket
"getpeername", // Retrieve the address of the peer connected to a socket
"listen", // Listen for connections on a socket
"recvmsg", // Receive messages from a socket
"gettid", // Get the thread ID of the calling thread
"getrandom", // Generate random numbers from the kernel's entropy pool
"futex", // Fast user-space locking (for thread synchronization)
"accept4", // Accept a connection on a socket with flags (non-blocking, etc.)
"epoll_create1", // Create an epoll instance (used for scalable I/O event notification)
"epoll_wait", // Wait for I/O events on an epoll instance
"epoll_pwait", // Similar to epoll_wait but allows signal mask modification
"epoll_ctl", // Control interface for epoll, used to add/remove/watch file descriptors
"eventfd2", // Create a file descriptor for event notifications
"inotify_init1", // Initialize inotify (used to monitor filesystem changes)
"inotify_add_watch", // Add a watch for filesystem events (e.g., file changes)
"inotify_rm_watch", // Remove a watch from inotify
"sched_getaffinity", // Get the CPU affinity mask for a thread (which CPUs the thread can run on)
"seccomp", // Load or manipulate a seccomp filter (for syscall restriction)
"gettimeofday", // Get the current time
"clock_gettime", // Retrieve the time of the specified clock (e.g., system uptime)
"fork", // Create a new process by duplicating the calling process
"memfd_create", // Create an anonymous file in memory (used for file-backed memory regions)
"getdents", // Read directory entries from a file descriptor
"getdents64", // Read directory entries (64-bit version, used on 64-bit systems)
},
},
},
},
}

// Load it. This will set no_new_privs before loading.
if err := seccomp.LoadFilter(filter); err != nil {
fmt.Println("failed to load filter: ", err)

Check failure on line 667 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L667

use of `fmt.Println` forbidden by pattern `^(fmt\.Print(|f|ln)|print|println)$` (forbidigo)

Check failure on line 667 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L667

use of `fmt.Println` forbidden by pattern `^(fmt\.Print(|f|ln)|print|println)$` (forbidigo)
return err

Check failure on line 668 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L668

return with no blank line before (nlreturn)

Check failure on line 668 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L668

return with no blank line before (nlreturn)
}
fmt.Println("Seccomp filter applied successfully")

Check failure on line 670 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L670

use of `fmt.Println` forbidden by pattern `^(fmt\.Print(|f|ln)|print|println)$` (forbidigo)

Check failure on line 670 in main.go

View check run for this annotation

Cirrus CI / Go Lint

main.go#L670

use of `fmt.Println` forbidden by pattern `^(fmt\.Print(|f|ln)|print|println)$` (forbidigo)

return nil
}

Check failure on line 673 in main.go

View check run for this annotation

Cirrus CI / Go Lint Mandatory

main.go#L673

File is not `gofumpt`-ed (gofumpt)
Loading