cmd: continuing linter fixes

This commit is contained in:
2025-11-16 02:54:02 -08:00
parent 0f77bd49dc
commit 3ad562b6fa
29 changed files with 319 additions and 175 deletions

View File

@@ -242,6 +242,10 @@ linters:
check-type-assertions: true check-type-assertions: true
exclude-functions: exclude-functions:
- (*git.wntrmute.dev/kyle/goutils/sbuf.Buffer).Write - (*git.wntrmute.dev/kyle/goutils/sbuf.Buffer).Write
- git.wntrmute.dev/kyle/goutils/lib.Warn
- git.wntrmute.dev/kyle/goutils/lib.Warnx
- git.wntrmute.dev/kyle/goutils/lib.Err
- git.wntrmute.dev/kyle/goutils/lib.Errx
exhaustive: exhaustive:
# Program elements to check for exhaustiveness. # Program elements to check for exhaustiveness.
@@ -333,6 +337,12 @@ linters:
# https://github.com/godoc-lint/godoc-lint?tab=readme-ov-file#no-unused-link # https://github.com/godoc-lint/godoc-lint?tab=readme-ov-file#no-unused-link
- no-unused-link - no-unused-link
gosec:
excludes:
- G104 # handled by errcheck
- G301
- G306
govet: govet:
# Enable all analyzers. # Enable all analyzers.
# Default: false # Default: false
@@ -368,6 +378,12 @@ linters:
- os.WriteFile - os.WriteFile
- prometheus.ExponentialBuckets.* - prometheus.ExponentialBuckets.*
- prometheus.LinearBuckets - prometheus.LinearBuckets
ignored-numbers:
- 1
- 2
- 3
- 4
- 8
nakedret: nakedret:
# Make an issue if func has more lines of code than this setting, and it has naked returns. # Make an issue if func has more lines of code than this setting, and it has naked returns.
@@ -436,6 +452,8 @@ linters:
# Omit embedded fields from selector expression. # Omit embedded fields from selector expression.
# https://staticcheck.dev/docs/checks/#QF1008 # https://staticcheck.dev/docs/checks/#QF1008
- -QF1008 - -QF1008
# We often explicitly enable old/deprecated ciphers for research.
- -SA1019
usetesting: usetesting:
# Enable/disable `os.TempDir()` detections. # Enable/disable `os.TempDir()` detections.

View File

@@ -458,8 +458,6 @@ func GetKeyDERFromPEM(in []byte, password []byte) ([]byte, error) {
} }
if procType, ok := keyDER.Headers["Proc-Type"]; ok && strings.Contains(procType, "ENCRYPTED") { if procType, ok := keyDER.Headers["Proc-Type"]; ok && strings.Contains(procType, "ENCRYPTED") {
if password != nil { if password != nil {
// nolintlint requires rationale:
//nolint:staticcheck // legacy RFC1423 PEM encryption supported for backward compatibility when caller supplies a password
return x509.DecryptPEMBlock(keyDER, password) return x509.DecryptPEMBlock(keyDER, password)
} }
return nil, certerr.DecodeError(certerr.ErrorSourcePrivateKey, certerr.ErrEncryptedPrivateKey) return nil, certerr.DecodeError(certerr.ErrorSourcePrivateKey, certerr.ErrEncryptedPrivateKey)

View File

@@ -1,6 +1,7 @@
package main package main
import ( import (
"context"
"crypto/tls" "crypto/tls"
"encoding/pem" "encoding/pem"
"flag" "flag"
@@ -22,22 +23,26 @@ func main() {
server += ":443" server += ":443"
} }
var chain string d := &tls.Dialer{Config: &tls.Config{}} // #nosec G402
nc, err := d.DialContext(context.Background(), "tcp", server)
conn, err := tls.Dial("tcp", server, nil)
die.If(err) die.If(err)
conn, ok := nc.(*tls.Conn)
if !ok {
die.With("invalid TLS connection (not a *tls.Conn)")
}
defer conn.Close()
details := conn.ConnectionState() details := conn.ConnectionState()
var chainSb30 strings.Builder var chain strings.Builder
for _, cert := range details.PeerCertificates { for _, cert := range details.PeerCertificates {
p := pem.Block{ p := pem.Block{
Type: "CERTIFICATE", Type: "CERTIFICATE",
Bytes: cert.Raw, Bytes: cert.Raw,
} }
chainSb30.WriteString(string(pem.EncodeToMemory(&p))) chain.Write(pem.EncodeToMemory(&p))
} }
chain += chainSb30.String()
fmt.Fprintln(os.Stdout, chain) fmt.Fprintln(os.Stdout, chain.String())
} }
} }

View File

@@ -3,6 +3,7 @@ package main
import ( import (
"bytes" "bytes"
"context"
"crypto/dsa" "crypto/dsa"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
@@ -246,18 +247,34 @@ func displayAllCerts(in []byte, leafOnly bool) {
func displayAllCertsWeb(uri string, leafOnly bool) { func displayAllCertsWeb(uri string, leafOnly bool) {
ci := getConnInfo(uri) ci := getConnInfo(uri)
conn, err := tls.Dial("tcp", ci.Addr, permissiveConfig()) d := &tls.Dialer{Config: permissiveConfig()}
nc, err := d.DialContext(context.Background(), "tcp", ci.Addr)
if err != nil { if err != nil {
_, _ = lib.Warn(err, "couldn't connect to %s", ci.Addr) _, _ = lib.Warn(err, "couldn't connect to %s", ci.Addr)
return return
} }
conn, ok := nc.(*tls.Conn)
if !ok {
_, _ = lib.Warnx("invalid TLS connection (not a *tls.Conn)")
return
}
defer conn.Close() defer conn.Close()
state := conn.ConnectionState() state := conn.ConnectionState()
conn.Close() if err = conn.Close(); err != nil {
_, _ = lib.Warn(err, "couldn't close TLS connection")
}
conn, err = tls.Dial("tcp", ci.Addr, verifyConfig(ci.Host)) d = &tls.Dialer{Config: verifyConfig(ci.Host)}
nc, err = d.DialContext(context.Background(), "tcp", ci.Addr)
if err == nil { if err == nil {
conn, ok = nc.(*tls.Conn)
if !ok {
_, _ = lib.Warnx("invalid TLS connection (not a *tls.Conn)")
return
}
err = conn.VerifyHostname(ci.Host) err = conn.VerifyHostname(ci.Host)
if err == nil { if err == nil {
state = conn.ConnectionState() state = conn.ConnectionState()
@@ -293,6 +310,32 @@ func displayAllCertsWeb(uri string, leafOnly bool) {
} }
} }
func shouldReadStdin(argc int, argv []string) bool {
if argc == 0 {
return true
}
if argc == 1 && argv[0] == "-" {
return true
}
return false
}
func readStdin(leafOnly bool) {
certs, err := io.ReadAll(os.Stdin)
if err != nil {
_, _ = lib.Warn(err, "couldn't read certificates from standard input")
os.Exit(1)
}
// This is needed for getting certs from JSON/jq.
certs = bytes.TrimSpace(certs)
certs = bytes.ReplaceAll(certs, []byte(`\n`), []byte{0xa})
certs = bytes.Trim(certs, `"`)
displayAllCerts(certs, leafOnly)
}
func main() { func main() {
var leafOnly bool var leafOnly bool
flag.BoolVar(&showHash, "d", false, "show hashes of raw DER contents") flag.BoolVar(&showHash, "d", false, "show hashes of raw DER contents")
@@ -300,32 +343,23 @@ func main() {
flag.BoolVar(&leafOnly, "l", false, "only show the leaf certificate") flag.BoolVar(&leafOnly, "l", false, "only show the leaf certificate")
flag.Parse() flag.Parse()
if flag.NArg() == 0 || (flag.NArg() == 1 && flag.Arg(0) == "-") { if shouldReadStdin(flag.NArg(), flag.Args()) {
certs, err := io.ReadAll(os.Stdin) readStdin(leafOnly)
if err != nil { return
_, _ = lib.Warn(err, "couldn't read certificates from standard input") }
os.Exit(1)
}
// This is needed for getting certs from JSON/jq. for _, filename := range flag.Args() {
certs = bytes.TrimSpace(certs) fmt.Fprintf(os.Stdout, "--%s ---%s", filename, "\n")
certs = bytes.ReplaceAll(certs, []byte(`\n`), []byte{0xa}) if strings.HasPrefix(filename, "https://") {
certs = bytes.Trim(certs, `"`) displayAllCertsWeb(filename, leafOnly)
displayAllCerts(certs, leafOnly) } else {
} else { in, err := os.ReadFile(filename)
for _, filename := range flag.Args() { if err != nil {
fmt.Fprintf(os.Stdout, "--%s ---%s", filename, "\n") _, _ = lib.Warn(err, "couldn't read certificate")
if strings.HasPrefix(filename, "https://") { continue
displayAllCertsWeb(filename, leafOnly)
} else {
in, err := os.ReadFile(filename)
if err != nil {
_, _ = lib.Warn(err, "couldn't read certificate")
continue
}
displayAllCerts(in, leafOnly)
} }
displayAllCerts(in, leafOnly)
} }
} }
} }

View File

@@ -13,6 +13,11 @@ import (
// following two lifted from CFSSL, (replace-regexp "\(.+\): \(.+\)," // following two lifted from CFSSL, (replace-regexp "\(.+\): \(.+\),"
// "\2: \1,") // "\2: \1,")
const (
sSHA256 = "SHA256"
sSHA512 = "SHA512"
)
var keyUsage = map[x509.KeyUsage]string{ var keyUsage = map[x509.KeyUsage]string{
x509.KeyUsageDigitalSignature: "digital signature", x509.KeyUsageDigitalSignature: "digital signature",
x509.KeyUsageContentCommitment: "content committment", x509.KeyUsageContentCommitment: "content committment",
@@ -70,19 +75,19 @@ func sigAlgoHash(a x509.SignatureAlgorithm) string {
case x509.SHA1WithRSA, x509.ECDSAWithSHA1, x509.DSAWithSHA1: case x509.SHA1WithRSA, x509.ECDSAWithSHA1, x509.DSAWithSHA1:
return "SHA1" return "SHA1"
case x509.SHA256WithRSA, x509.ECDSAWithSHA256, x509.DSAWithSHA256: case x509.SHA256WithRSA, x509.ECDSAWithSHA256, x509.DSAWithSHA256:
return "SHA256" return sSHA256
case x509.SHA256WithRSAPSS: case x509.SHA256WithRSAPSS:
return "SHA256" return sSHA256
case x509.SHA384WithRSA, x509.ECDSAWithSHA384: case x509.SHA384WithRSA, x509.ECDSAWithSHA384:
return "SHA384" return "SHA384"
case x509.SHA384WithRSAPSS: case x509.SHA384WithRSAPSS:
return "SHA384" return "SHA384"
case x509.SHA512WithRSA, x509.ECDSAWithSHA512: case x509.SHA512WithRSA, x509.ECDSAWithSHA512:
return "SHA512" return sSHA512
case x509.SHA512WithRSAPSS: case x509.SHA512WithRSAPSS:
return "SHA512" return sSHA512
case x509.PureEd25519: case x509.PureEd25519:
return "SHA512" return sSHA512
case x509.UnknownSignatureAlgorithm: case x509.UnknownSignatureAlgorithm:
return "unknown hash algorithm" return "unknown hash algorithm"
default: default:
@@ -144,14 +149,14 @@ func dumpHex(in []byte) string {
func permissiveConfig() *tls.Config { func permissiveConfig() *tls.Config {
return &tls.Config{ return &tls.Config{
InsecureSkipVerify: true, InsecureSkipVerify: true,
} } // #nosec G402
} }
// verifyConfig returns a config that will verify the connection. // verifyConfig returns a config that will verify the connection.
func verifyConfig(hostname string) *tls.Config { func verifyConfig(hostname string) *tls.Config {
return &tls.Config{ return &tls.Config{
ServerName: hostname, ServerName: hostname,
} } // #nosec G402
} }
type connInfo struct { type connInfo struct {

View File

@@ -2,6 +2,7 @@ package main
import ( import (
"bufio" "bufio"
"context"
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
@@ -57,7 +58,7 @@ var modes = ssh.TerminalModes{
} }
func sshAgent() ssh.AuthMethod { func sshAgent() ssh.AuthMethod {
a, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")) a, err := (&net.Dialer{}).DialContext(context.Background(), "unix", os.Getenv("SSH_AUTH_SOCK"))
if err == nil { if err == nil {
return ssh.PublicKeysCallback(agent.NewClient(a).Signers) return ssh.PublicKeysCallback(agent.NewClient(a).Signers)
} }
@@ -116,7 +117,7 @@ func exec(wg *sync.WaitGroup, user, host string, commands []string) {
} }
shutdown = append(shutdown, session.Close) shutdown = append(shutdown, session.Close)
if err := session.RequestPty("xterm", 80, 40, modes); err != nil { if err = session.RequestPty("xterm", 80, 40, modes); err != nil {
session.Close() session.Close()
logError(host, err, "request for pty failed") logError(host, err, "request for pty failed")
return return

View File

@@ -26,7 +26,7 @@ func setupFile(hdr *tar.Header, file *os.File) error {
if verbose { if verbose {
fmt.Printf("\tchmod %0#o\n", hdr.Mode) fmt.Printf("\tchmod %0#o\n", hdr.Mode)
} }
err := file.Chmod(os.FileMode(hdr.Mode)) err := file.Chmod(os.FileMode(hdr.Mode & 0xFFFFFFFF)) // #nosec G115
if err != nil { if err != nil {
return err return err
} }
@@ -55,7 +55,9 @@ func processFile(tfr *tar.Reader, hdr *tar.Header, top string) error {
if verbose { if verbose {
fmt.Println(hdr.Name) fmt.Println(hdr.Name)
} }
filePath := filepath.Clean(filepath.Join(top, hdr.Name)) filePath := filepath.Clean(filepath.Join(top, hdr.Name))
switch hdr.Typeflag { switch hdr.Typeflag {
case tar.TypeReg: case tar.TypeReg:
file, err := os.Create(filePath) file, err := os.Create(filePath)
@@ -109,7 +111,7 @@ func processFile(tfr *tar.Reader, hdr *tar.Header, top string) error {
return err return err
} }
case tar.TypeDir: case tar.TypeDir:
err := os.MkdirAll(filePath, os.FileMode(hdr.Mode)) err := os.MkdirAll(filePath, os.FileMode(hdr.Mode&0xFFFFFFFF)) // #nosec G115
if err != nil { if err != nil {
return err return err
} }
@@ -261,8 +263,9 @@ func main() {
die.If(err) die.If(err)
tfr := tar.NewReader(r) tfr := tar.NewReader(r)
var hdr *tar.Header
for { for {
hdr, err := tfr.Next() hdr, err = tfr.Next()
if errors.Is(err, io.EOF) { if errors.Is(err, io.EOF) {
break break
} }

View File

@@ -47,7 +47,7 @@ func main() {
Bytes: out, Bytes: out,
} }
err = os.WriteFile(fileName+".pub", pem.EncodeToMemory(p), 0o644) err = os.WriteFile(fileName+".pub", pem.EncodeToMemory(p), 0o644) // #nosec G306
die.If(err) die.If(err)
fmt.Fprintf(os.Stdout, "[+] wrote %s.\n", fileName+".pub") fmt.Fprintf(os.Stdout, "[+] wrote %s.\n", fileName+".pub")
} }

View File

@@ -1,6 +1,7 @@
package main package main
import ( import (
"context"
"flag" "flag"
"fmt" "fmt"
"io" "io"
@@ -152,7 +153,7 @@ func rsync(syncDir, target, excludeFile string, verboseRsync bool) error {
return err return err
} }
cmd := exec.Command(path, args...) cmd := exec.CommandContext(context.Background(), path, args...)
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
return cmd.Run() return cmd.Run()
@@ -218,7 +219,7 @@ func main() {
if excludeFile != "" { if excludeFile != "" {
defer func() { defer func() {
log.Infof("removing exclude file %s", excludeFile) log.Infof("removing exclude file %s", excludeFile)
if err := os.Remove(excludeFile); err != nil { if rmErr := os.Remove(excludeFile); rmErr != nil {
log.Warningf("failed to remove temp file %s", excludeFile) log.Warningf("failed to remove temp file %s", excludeFile)
} }
}() }()

View File

@@ -30,7 +30,7 @@ func openImage(imageFile string) (*os.File, []byte, error) {
return nil, nil, err return nil, nil, err
} }
if _, err := f.Seek(0, 0); err != nil { if _, err = f.Seek(0, 0); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -103,12 +103,12 @@ func main() {
die.If(err) die.If(err)
if !bytes.Equal(deviceHash, hash) { if !bytes.Equal(deviceHash, hash) {
fmt.Fprintln(os.Stderr, "Hash mismatch:") buf := &bytes.Buffer{}
fmt.Fprintf(os.Stderr, "\t%s: %s\n", imageFile, hash) fmt.Fprintln(buf, "Hash mismatch:")
fmt.Fprintf(os.Stderr, "\t%s: %s\n", devicePath, deviceHash) fmt.Fprintf(buf, "\t%s: %s\n", imageFile, hash)
os.Exit(1) fmt.Fprintf(buf, "\t%s: %s\n", devicePath, deviceHash)
die.With(buf.String())
} }
debug.Println("OK") debug.Println("OK")
os.Exit(0)
} }

View File

@@ -1,6 +1,7 @@
package main package main
import ( import (
"errors"
"flag" "flag"
"fmt" "fmt"
"io" "io"
@@ -37,10 +38,11 @@ func dumpFile(path string, indentLevel int) error {
defer file.Close() defer file.Close()
fmt.Printf("%svar buffer = []byte{\n", indent.String()) fmt.Printf("%svar buffer = []byte{\n", indent.String())
var n int
for { for {
buf := make([]byte, 8) buf := make([]byte, 8)
n, err := file.Read(buf) n, err = file.Read(buf)
if err == io.EOF { if errors.Is(err, io.EOF) {
if n > 0 { if n > 0 {
fmt.Printf("%s", indent.String()) fmt.Printf("%s", indent.String())
printBytes(buf[:n]) printBytes(buf[:n])

View File

@@ -26,7 +26,7 @@ func main() {
path = flag.Arg(0) path = flag.Arg(0)
} }
fillByte := uint8(*fill) fillByte := uint8(*fill & 0xff) // #nosec G115 clearing out of bounds bits
buf := make([]byte, pageSize) buf := make([]byte, pageSize)
for i := range pageSize { for i := range pageSize {

View File

@@ -72,9 +72,7 @@ func main() {
if end < start { if end < start {
fmt.Fprintln(os.Stderr, "[!] end < start, swapping values") fmt.Fprintln(os.Stderr, "[!] end < start, swapping values")
tmp := end start, end = end, start
end = start
start = tmp
} }
var fmtStr string var fmtStr string

View File

@@ -1,6 +1,7 @@
package main package main
import ( import (
"context"
"flag" "flag"
"fmt" "fmt"
"log" "log"
@@ -8,7 +9,8 @@ import (
) )
func lookupHost(host string) error { func lookupHost(host string) error {
cname, err := net.LookupCNAME(host) r := &net.Resolver{}
cname, err := r.LookupCNAME(context.Background(), host)
if err != nil { if err != nil {
return err return err
} }
@@ -18,7 +20,7 @@ func lookupHost(host string) error {
host = cname host = cname
} }
addrs, err := net.LookupHost(host) addrs, err := r.LookupHost(context.Background(), host)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -11,7 +11,10 @@ based on whether the source filename ends in ".gz".
Flags: Flags:
-l level Compression level (0-9). Only meaninful when -l level Compression level (0-9). Only meaninful when
compressing a file. compressing a file.
-u Do not restrict the size during decompression. As
a safeguard against gzip bombs, the maximum size
allowed is 32 * the compressed file size.

View File

@@ -40,26 +40,42 @@ func compress(path, target string, level int) error {
return nil return nil
} }
func uncompress(path, target string) error { func uncompress(path, target string, unrestrict bool) error {
sourceFile, err := os.Open(path) sourceFile, err := os.Open(path)
if err != nil { if err != nil {
return fmt.Errorf("opening file for read: %w", err) return fmt.Errorf("opening file for read: %w", err)
} }
defer sourceFile.Close() defer sourceFile.Close()
fi, err := sourceFile.Stat()
if err != nil {
return fmt.Errorf("reading file stats: %w", err)
}
maxDecompressionSize := fi.Size() * 32
gzipUncompressor, err := gzip.NewReader(sourceFile) gzipUncompressor, err := gzip.NewReader(sourceFile)
if err != nil { if err != nil {
return fmt.Errorf("reading gzip headers: %w", err) return fmt.Errorf("reading gzip headers: %w", err)
} }
defer gzipUncompressor.Close() defer gzipUncompressor.Close()
var reader io.Reader = &io.LimitedReader{
R: gzipUncompressor,
N: maxDecompressionSize,
}
if unrestrict {
reader = gzipUncompressor
}
destFile, err := os.Create(target) destFile, err := os.Create(target)
if err != nil { if err != nil {
return fmt.Errorf("opening file for write: %w", err) return fmt.Errorf("opening file for write: %w", err)
} }
defer destFile.Close() defer destFile.Close()
_, err = io.Copy(destFile, gzipUncompressor) _, err = io.Copy(destFile, reader)
if err != nil { if err != nil {
return fmt.Errorf("uncompressing file: %w", err) return fmt.Errorf("uncompressing file: %w", err)
} }
@@ -87,8 +103,8 @@ func isDir(path string) bool {
file, err := os.Open(path) file, err := os.Open(path)
if err == nil { if err == nil {
defer file.Close() defer file.Close()
stat, err := file.Stat() stat, err2 := file.Stat()
if err != nil { if err2 != nil {
return false return false
} }
@@ -132,8 +148,11 @@ func main() {
var level int var level int
var path string var path string
var target = "." var target = "."
var err error
var unrestrict bool
flag.IntVar(&level, "l", flate.DefaultCompression, "compression level") flag.IntVar(&level, "l", flate.DefaultCompression, "compression level")
flag.BoolVar(&unrestrict, "u", false, "do not restrict decompression")
flag.Parse() flag.Parse()
if flag.NArg() < 1 || flag.NArg() > 2 { if flag.NArg() < 1 || flag.NArg() > 2 {
@@ -147,30 +166,31 @@ func main() {
} }
if strings.HasSuffix(path, gzipExt) { if strings.HasSuffix(path, gzipExt) {
target, err := pathForUncompressing(path, target) target, err = pathForUncompressing(path, target)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err) fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1) os.Exit(1)
} }
err = uncompress(path, target) err = uncompress(path, target, unrestrict)
if err != nil { if err != nil {
os.Remove(target) os.Remove(target)
fmt.Fprintf(os.Stderr, "%s\n", err) fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1) os.Exit(1)
} }
} else { return
target, err := pathForCompressing(path, target) }
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}
err = compress(path, target, level) target, err = pathForCompressing(path, target)
if err != nil { if err != nil {
os.Remove(target) fmt.Fprintf(os.Stderr, "%s\n", err)
fmt.Fprintf(os.Stderr, "%s\n", err) os.Exit(1)
os.Exit(1) }
}
err = compress(path, target, level)
if err != nil {
os.Remove(target)
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
} }
} }

View File

@@ -32,7 +32,7 @@ func main() {
if p == nil { if p == nil {
lib.Errx(lib.ExitFailure, "%s isn't a PEM-encoded file", flag.Arg(0)) lib.Errx(lib.ExitFailure, "%s isn't a PEM-encoded file", flag.Arg(0))
} }
if _, err := os.Stdout.Write(p.Bytes); err != nil { if _, err = os.Stdout.Write(p.Bytes); err != nil {
lib.Err(lib.ExitFailure, err, "writing body") lib.Err(lib.ExitFailure, err, "writing body")
} }
} }

View File

@@ -27,7 +27,8 @@ func main() {
break break
} }
cert, err := x509.ParseCertificate(p.Bytes) var cert *x509.Certificate
cert, err = x509.ParseCertificate(p.Bytes)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "[!] %s: %v\n", fileName, err) fmt.Fprintf(os.Stderr, "[!] %s: %v\n", fileName, err)
break break

View File

@@ -1,6 +1,7 @@
package main package main
import ( import (
"context"
"flag" "flag"
"fmt" "fmt"
"io" "io"
@@ -76,7 +77,13 @@ func main() {
continue continue
} }
resp, err := http.Get(remote) req, reqErr := http.NewRequestWithContext(context.Background(), http.MethodGet, remote, nil)
if reqErr != nil {
_, _ = lib.Warn(reqErr, "building request for %s", remote)
continue
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil { if err != nil {
_, _ = lib.Warn(err, "fetching %s", remote) _, _ = lib.Warn(err, "fetching %s", remote)
continue continue

View File

@@ -18,7 +18,7 @@ func rollDie(count, sides int) []int {
var rolls []int var rolls []int
for range count { for range count {
roll := rand.IntN(sides) + 1 roll := rand.IntN(sides) + 1 // #nosec G404
sum += roll sum += roll
rolls = append(rolls, roll) rolls = append(rolls, roll)
} }

View File

@@ -75,13 +75,14 @@ func walkFile(path string, _ os.FileInfo, err error) error {
for _, importSpec := range f.Imports { for _, importSpec := range f.Imports {
importPath := strings.Trim(importSpec.Path.Value, `"`) importPath := strings.Trim(importSpec.Path.Value, `"`)
if stdLibRegexp.MatchString(importPath) { switch {
case stdLibRegexp.MatchString(importPath):
debug.Println("standard lib:", importPath) debug.Println("standard lib:", importPath)
continue continue
} else if strings.HasPrefix(importPath, project) { case strings.HasPrefix(importPath, project):
debug.Println("internal import:", importPath) debug.Println("internal import:", importPath)
continue continue
} else if strings.HasPrefix(importPath, "golang.org/") { case strings.HasPrefix(importPath, "golang.org/"):
debug.Println("extended lib:", importPath) debug.Println("extended lib:", importPath)
continue continue
} }

View File

@@ -5,7 +5,7 @@ import (
"crypto" "crypto"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/rsa" "crypto/rsa"
"crypto/sha1" "crypto/sha1" // #nosec G505
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/asn1" "encoding/asn1"
@@ -20,6 +20,11 @@ import (
"git.wntrmute.dev/kyle/goutils/lib" "git.wntrmute.dev/kyle/goutils/lib"
) )
const (
keyTypeRSA = "RSA"
keyTypeECDSA = "ECDSA"
)
func usage(w io.Writer) { func usage(w io.Writer) {
fmt.Fprintf(w, `ski: print subject key info for PEM-encoded files fmt.Fprintf(w, `ski: print subject key info for PEM-encoded files
@@ -94,10 +99,10 @@ func parseKey(data []byte) ([]byte, string) {
switch p := privInterface.(type) { switch p := privInterface.(type) {
case *rsa.PrivateKey: case *rsa.PrivateKey:
priv = p priv = p
kt = "RSA" kt = keyTypeRSA
case *ecdsa.PrivateKey: case *ecdsa.PrivateKey:
priv = p priv = p
kt = "ECDSA" kt = keyTypeECDSA
default: default:
die.With("unknown private key type %T", privInterface) die.With("unknown private key type %T", privInterface)
} }
@@ -116,9 +121,9 @@ func parseCertificate(data []byte) ([]byte, string) {
var kt string var kt string
switch pub.(type) { switch pub.(type) {
case *rsa.PublicKey: case *rsa.PublicKey:
kt = "RSA" kt = keyTypeRSA
case *ecdsa.PublicKey: case *ecdsa.PublicKey:
kt = "ECDSA" kt = keyTypeECDSA
default: default:
die.With("unknown public key type %T", pub) die.With("unknown public key type %T", pub)
} }
@@ -136,9 +141,9 @@ func parseCSR(data []byte) ([]byte, string) {
var kt string var kt string
switch pub.(type) { switch pub.(type) {
case *rsa.PublicKey: case *rsa.PublicKey:
kt = "RSA" kt = keyTypeRSA
case *ecdsa.PublicKey: case *ecdsa.PublicKey:
kt = "ECDSA" kt = keyTypeECDSA
default: default:
die.With("unknown public key type %T", pub) die.With("unknown public key type %T", pub)
} }
@@ -186,7 +191,7 @@ func main() {
continue continue
} }
pubHash := sha1.Sum(subPKI.SubjectPublicKey.Bytes) pubHash := sha1.Sum(subPKI.SubjectPublicKey.Bytes) // #nosec G401 this is the standard
pubHashString := dumpHex(pubHash[:]) pubHashString := dumpHex(pubHash[:])
if ski == "" { if ski == "" {
ski = pubHashString ski = pubHashString

View File

@@ -1,6 +1,7 @@
package main package main
import ( import (
"context"
"flag" "flag"
"io" "io"
"net" "net"
@@ -10,7 +11,7 @@ import (
) )
func proxy(conn net.Conn, inside string) error { func proxy(conn net.Conn, inside string) error {
proxyConn, err := net.Dial("tcp", inside) proxyConn, err := (&net.Dialer{}).DialContext(context.Background(), "tcp", inside)
if err != nil { if err != nil {
return err return err
} }
@@ -31,18 +32,20 @@ func main() {
flag.StringVar(&inside, "p", "4000", "inside port") flag.StringVar(&inside, "p", "4000", "inside port")
flag.Parse() flag.Parse()
l, err := net.Listen("tcp", "0.0.0.0:"+outside) lc := &net.ListenConfig{}
l, err := lc.Listen(context.Background(), "tcp", "0.0.0.0:"+outside)
die.If(err) die.If(err)
for { for {
conn, err := l.Accept() var conn net.Conn
conn, err = l.Accept()
if err != nil { if err != nil {
_, _ = lib.Warn(err, "accept failed") _, _ = lib.Warn(err, "accept failed")
continue continue
} }
go func() { go func() {
if err := proxy(conn, "127.0.0.1:"+inside); err != nil { if err = proxy(conn, "127.0.0.1:"+inside); err != nil {
_, _ = lib.Warn(err, "proxy error") _, _ = lib.Warn(err, "proxy error")
} }
}() }()

View File

@@ -1,6 +1,7 @@
package main package main
import ( import (
"context"
"crypto/rand" "crypto/rand"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
@@ -15,7 +16,7 @@ import (
) )
func main() { func main() {
cfg := &tls.Config{} cfg := &tls.Config{} // #nosec G402
var sysRoot, listenAddr, certFile, keyFile string var sysRoot, listenAddr, certFile, keyFile string
var verify bool var verify bool
@@ -46,7 +47,8 @@ func main() {
} }
cfg.Certificates = append(cfg.Certificates, cert) cfg.Certificates = append(cfg.Certificates, cert)
if sysRoot != "" { if sysRoot != "" {
pemList, err := os.ReadFile(sysRoot) var pemList []byte
pemList, err = os.ReadFile(sysRoot)
die.If(err) die.If(err)
roots := x509.NewCertPool() roots := x509.NewCertPool()
@@ -58,14 +60,16 @@ func main() {
cfg.RootCAs = roots cfg.RootCAs = roots
} }
l, err := net.Listen("tcp", listenAddr) lc := &net.ListenConfig{}
l, err := lc.Listen(context.Background(), "tcp", listenAddr)
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Println(err.Error())
os.Exit(1) os.Exit(1)
} }
for { for {
conn, err := l.Accept() var conn net.Conn
conn, err = l.Accept()
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Println(err.Error())
continue continue
@@ -79,7 +83,7 @@ func handleConn(conn net.Conn, cfg *tls.Config) {
defer conn.Close() defer conn.Close()
raddr := conn.RemoteAddr() raddr := conn.RemoteAddr()
tconn := tls.Server(conn, cfg) tconn := tls.Server(conn, cfg)
if err := tconn.Handshake(); err != nil { if err := tconn.HandshakeContext(context.Background()); err != nil {
fmt.Printf("[+] %v: failed to complete handshake: %v\n", raddr, err) fmt.Printf("[+] %v: failed to complete handshake: %v\n", raddr, err)
return return
} }

View File

@@ -1,6 +1,7 @@
package main package main
import ( import (
"context"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
@@ -13,7 +14,7 @@ import (
) )
func main() { func main() {
var cfg = &tls.Config{} var cfg = &tls.Config{} // #nosec G402
var sysRoot, serverName string var sysRoot, serverName string
flag.StringVar(&sysRoot, "ca", "", "provide an alternate CA bundle") flag.StringVar(&sysRoot, "ca", "", "provide an alternate CA bundle")
@@ -43,10 +44,13 @@ func main() {
if err != nil { if err != nil {
site += ":443" site += ":443"
} }
conn, err := tls.Dial("tcp", site, cfg) d := &tls.Dialer{Config: cfg}
if err != nil { nc, err := d.DialContext(context.Background(), "tcp", site)
fmt.Println(err.Error()) die.If(err)
os.Exit(1)
conn, ok := nc.(*tls.Conn)
if !ok {
die.With("invalid TLS connection (not a *tls.Conn)")
} }
cs := conn.ConnectionState() cs := conn.ConnectionState()
@@ -62,6 +66,7 @@ func main() {
err = os.WriteFile(site+".pem", chain, 0644) err = os.WriteFile(site+".pem", chain, 0644)
die.If(err) die.If(err)
fmt.Printf("[+] wrote %s.pem.\n", site) fmt.Printf("[+] wrote %s.pem.\n", site)
} }
} }

View File

@@ -1,10 +1,14 @@
package main package main
import ( import (
"context"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"fmt" "fmt"
"os" "os"
"git.wntrmute.dev/kyle/goutils/certlib/hosts"
"git.wntrmute.dev/kyle/goutils/die"
) )
func main() { func main() {
@@ -13,16 +17,23 @@ func main() {
os.Exit(1) os.Exit(1)
} }
hostPort := os.Args[1] hostPort, err := hosts.ParseHost(os.Args[1])
conn, err := tls.Dial("tcp", hostPort, &tls.Config{ die.If(err)
InsecureSkipVerify: true,
})
if err != nil { d := &tls.Dialer{Config: &tls.Config{
fmt.Printf("Failed to connect to the TLS server: %v\n", err) InsecureSkipVerify: true,
os.Exit(1) }} // #nosec G402
nc, err := d.DialContext(context.Background(), "tcp", hostPort.String())
die.If(err)
conn, ok := nc.(*tls.Conn)
if !ok {
die.With("invalid TLS connection (not a *tls.Conn)")
} }
defer conn.Close() defer conn.Close()
state := conn.ConnectionState() state := conn.ConnectionState()
printConnectionDetails(state) printConnectionDetails(state)
} }

View File

@@ -253,15 +253,16 @@ func main() {
showTime(time.Now()) showTime(time.Now())
os.Exit(0) os.Exit(0)
case 1: case 1:
if flag.Arg(0) == "-" { switch {
case flag.Arg(0) == "-":
s := bufio.NewScanner(os.Stdin) s := bufio.NewScanner(os.Stdin)
for s.Scan() { for s.Scan() {
times = append(times, s.Text()) times = append(times, s.Text())
} }
} else if flag.Arg(0) == "help" { case flag.Arg(0) == "help":
usageExamples() usageExamples()
} else { default:
times = flag.Args() times = flag.Args()
} }
default: default:

View File

@@ -11,9 +11,8 @@ import (
type empty struct{} type empty struct{}
func errorf(format string, args ...any) { func errorf(path string, err error) {
format += "\n" fmt.Fprintf(os.Stderr, "%s FAILED: %s\n", path, err)
fmt.Fprintf(os.Stderr, format, args...)
} }
func usage(w io.Writer) { func usage(w io.Writer) {
@@ -45,14 +44,14 @@ func main() {
path := "stdin" path := "stdin"
in, err := io.ReadAll(os.Stdin) in, err := io.ReadAll(os.Stdin)
if err != nil { if err != nil {
errorf("%s FAILED: %s", path, err) errorf(path, err)
os.Exit(1) os.Exit(1)
} }
var e empty var e empty
err = yaml.Unmarshal(in, &e) err = yaml.Unmarshal(in, &e)
if err != nil { if err != nil {
errorf("%s FAILED: %s", path, err) errorf(path, err)
os.Exit(1) os.Exit(1)
} }
@@ -66,14 +65,14 @@ func main() {
for _, path := range flag.Args() { for _, path := range flag.Args() {
in, err := os.ReadFile(path) in, err := os.ReadFile(path)
if err != nil { if err != nil {
errorf("%s FAILED: %s", path, err) errorf(path, err)
continue continue
} }
var e empty var e empty
err = yaml.Unmarshal(in, &e) err = yaml.Unmarshal(in, &e)
if err != nil { if err != nil {
errorf("%s FAILED: %s", path, err) errorf(path, err)
continue continue
} }

View File

@@ -14,16 +14,16 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"git.wntrmute.dev/kyle/goutils/lib"
) )
const defaultDirectory = ".git/objects" const defaultDirectory = ".git/objects"
func errorf(format string, a ...any) { // maxDecompressedSize limits how many bytes we will decompress from a zlib
fmt.Fprintf(os.Stderr, format, a...) // stream to mitigate decompression bombs (gosec G110).
if format[len(format)-1] != '\n' { // Increase this if you expect larger objects.
fmt.Fprintf(os.Stderr, "\n") const maxDecompressedSize int64 = 64 << 30 // 64 GiB
}
}
func isDir(path string) bool { func isDir(path string) bool {
fi, err := os.Stat(path) fi, err := os.Stat(path)
@@ -48,17 +48,21 @@ func loadFile(path string) ([]byte, error) {
} }
defer zread.Close() defer zread.Close()
_, err = io.Copy(buf, zread) // Protect against decompression bombs by limiting how much we read.
if err != nil { lr := io.LimitReader(zread, maxDecompressedSize+1)
if _, err = buf.ReadFrom(lr); err != nil {
return nil, err return nil, err
} }
if int64(buf.Len()) > maxDecompressedSize {
return nil, fmt.Errorf("decompressed size exceeds limit (%d bytes)", maxDecompressedSize)
}
return buf.Bytes(), nil return buf.Bytes(), nil
} }
func showFile(path string) { func showFile(path string) {
fileData, err := loadFile(path) fileData, err := loadFile(path)
if err != nil { if err != nil {
errorf("%v", err) lib.Warn(err, "failed to load %s", path)
return return
} }
@@ -68,39 +72,71 @@ func showFile(path string) {
func searchFile(path string, search *regexp.Regexp) error { func searchFile(path string, search *regexp.Regexp) error {
file, err := os.Open(path) file, err := os.Open(path)
if err != nil { if err != nil {
errorf("%v", err) lib.Warn(err, "failed to open %s", path)
return err return err
} }
defer file.Close() defer file.Close()
zread, err := zlib.NewReader(file) zread, err := zlib.NewReader(file)
if err != nil { if err != nil {
errorf("%v", err) lib.Warn(err, "failed to decompress %s", path)
return err return err
} }
defer zread.Close() defer zread.Close()
zbuf := bufio.NewReader(zread) // Limit how much we scan to avoid DoS via huge decompression.
if search.MatchReader(zbuf) { lr := io.LimitReader(zread, maxDecompressedSize+1)
fileData, err := loadFile(path) zbuf := bufio.NewReader(lr)
if err != nil { if !search.MatchReader(zbuf) {
errorf("%v", err) return nil
return err
}
fmt.Printf("%s:\n%s\n", path, fileData)
} }
fileData, err := loadFile(path)
if err != nil {
lib.Warn(err, "failed to load %s", path)
return err
}
fmt.Printf("%s:\n%s\n", path, fileData)
return nil return nil
} }
func buildWalker(searchExpr *regexp.Regexp) filepath.WalkFunc { func buildWalker(searchExpr *regexp.Regexp) filepath.WalkFunc {
return func(path string, info os.FileInfo, _ error) error { return func(path string, info os.FileInfo, _ error) error {
if info.Mode().IsRegular() { if !info.Mode().IsRegular() {
return searchFile(path, searchExpr) return nil
} }
return nil return searchFile(path, searchExpr)
} }
} }
// runSearch compiles the search expression and processes the provided paths.
// It returns an error for fatal conditions; per-file errors are logged.
func runSearch(expr string) error {
search, err := regexp.Compile(expr)
if err != nil {
return fmt.Errorf("invalid regexp: %w", err)
}
pathList := flag.Args()
if len(pathList) == 0 {
pathList = []string{defaultDirectory}
}
for _, path := range pathList {
if isDir(path) {
if err2 := filepath.Walk(path, buildWalker(search)); err2 != nil {
return err2
}
continue
}
if err2 := searchFile(path, search); err2 != nil {
// Non-fatal: keep going, but report it.
lib.Warn(err2, "non-fatal error while searching files")
}
}
return nil
}
func main() { func main() {
flSearch := flag.String("s", "", "search string (should be an RE2 regular expression)") flSearch := flag.String("s", "", "search string (should be an RE2 regular expression)")
flag.Parse() flag.Parse()
@@ -109,29 +145,10 @@ func main() {
for _, path := range flag.Args() { for _, path := range flag.Args() {
showFile(path) showFile(path)
} }
} else { return
search, err := regexp.Compile(*flSearch) }
if err != nil {
errorf("Bad regexp: %v", err)
return
}
pathList := flag.Args() if err := runSearch(*flSearch); err != nil {
if len(pathList) == 0 { lib.Err(lib.ExitFailure, err, "failed to run search")
pathList = []string{defaultDirectory}
}
for _, path := range pathList {
if isDir(path) {
if err := filepath.Walk(path, buildWalker(search)); err != nil {
errorf("%v", err)
return
}
} else {
if err := searchFile(path, search); err != nil {
errorf("%v", err)
}
}
}
} }
} }