diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 2aded909..3446c847 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -20,7 +20,7 @@ jobs: 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.53.3 + version: v1.61.0 # Optional: working directory, useful for monorepos # working-directory: somedir diff --git a/.golangci.yml b/.golangci.yml index dd06181f..22a0f002 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -35,4 +35,4 @@ linters: - unparam - wastedassign - whitespace - - wsl + - wsl \ No newline at end of file diff --git a/pkg/derivation/derivation_test.go b/pkg/derivation/derivation_test.go index 5241cf9f..a6cce4ba 100644 --- a/pkg/derivation/derivation_test.go +++ b/pkg/derivation/derivation_test.go @@ -209,6 +209,7 @@ func TestWriter(t *testing.T) { } var sb strings.Builder + err = drv.WriteDerivation(&sb) if err != nil { panic(err) @@ -312,9 +313,11 @@ func TestValidate(t *testing.T) { // the first input derivation, and re-insert it with an empty key. k := "/nix/store/073gancjdr3z1scm2p553v0k3cxj2cpy-fix-tests-when-building-without-regex-supports.patch.drv" firstInputDrv, ok := drv.InputDerivations[k] + if !ok { panic("missing key") } + delete(drv.InputDerivations, k) drv.InputDerivations[""] = firstInputDrv diff --git a/pkg/derivation/encode.go b/pkg/derivation/encode.go index 098d0dad..19a1e7c3 100644 --- a/pkg/derivation/encode.go +++ b/pkg/derivation/encode.go @@ -106,10 +106,12 @@ func (d *Derivation) writeDerivation( outputNames := make([]string, len(d.Outputs)) { i := 0 + for k := range d.Outputs { outputNames[i] = k i++ } + sort.Strings(outputNames) } @@ -129,6 +131,7 @@ func (d *Derivation) writeDerivation( if !ok { return fmt.Errorf("unable to find replacement for %s, but replacement requested", replacement) } + inputDerivations[replacement] = outputNames } } @@ -138,10 +141,12 @@ func (d *Derivation) writeDerivation( inputDerivationPaths := make([]string, len(inputDerivations)) { i := 0 + for inputDerivationPath := range inputDerivations { inputDerivationPaths[i] = inputDerivationPath i++ } + sort.Strings(inputDerivationPaths) } @@ -150,10 +155,12 @@ func (d *Derivation) writeDerivation( envKeys := make([]string, len(d.Env)) { i := 0 + for k := range d.Env { envKeys[i] = k i++ } + sort.Strings(envKeys) } diff --git a/pkg/derivation/helpers.go b/pkg/derivation/helpers.go index afa5a2b4..5ec87feb 100644 --- a/pkg/derivation/helpers.go +++ b/pkg/derivation/helpers.go @@ -1,7 +1,6 @@ package derivation import ( - "reflect" "unsafe" ) @@ -16,12 +15,5 @@ func unsafeString(b []byte) string { // It's safe to use in situations like hash calculations or // writing into buffers. func unsafeBytes(s string) []byte { - return unsafe.Slice( - (*byte)( - unsafe.Pointer( - (*reflect.StringHeader)(unsafe.Pointer(&s)).Data, - ), - ), - len(s), - ) + return unsafe.Slice(unsafe.StringData(s), len(s)) } diff --git a/pkg/derivation/parser.go b/pkg/derivation/parser.go index d95cbfcf..de1f4333 100644 --- a/pkg/derivation/parser.go +++ b/pkg/derivation/parser.go @@ -70,19 +70,21 @@ func parseDerivation(derivationBytes []byte) (*Derivation, error) { // keep track of the previous path read (if any), so we detect // invalid encodings. prevOutputName := "" - err = arrayEach(value, func(value []byte, index int) error { + err = arrayEach(value, func(value []byte, _ int) error { output := &Output{} outputName := "" // Get every output field err := arrayEach(value, func(value []byte, index int) error { var err error + switch index { case 0: outputName, err = unquoteSlice(value) if err != nil { return err } + if outputName <= prevOutputName { return fmt.Errorf("invalid output order, %s <= %s", outputName, prevOutputName) } @@ -114,6 +116,7 @@ func parseDerivation(derivationBytes []byte) (*Derivation, error) { if outputName == "" { return fmt.Errorf("output name for %s may not be empty", output.Path) } + drv.Outputs[outputName] = output prevOutputName = outputName @@ -124,28 +127,31 @@ func parseDerivation(derivationBytes []byte) (*Derivation, error) { drv.InputDerivations = make(map[string][]string) // InputDerivations are always lexicographically sorted by their path prevInputDrvPath := "" - err = arrayEach(value, func(value []byte, index int) error { + err = arrayEach(value, func(value []byte, _ int) error { inputDrvPath := "" inputDrvNames := []string{} err := arrayEach(value, func(value []byte, index int) error { var err error + switch index { case 0: inputDrvPath, err = unquoteSlice(value) if err != nil { return err } + if inputDrvPath <= prevInputDrvPath { return fmt.Errorf("invalid input derivation order: %s <= %s", inputDrvPath, prevInputDrvPath) } case 1: - err := arrayEach(value, func(value []byte, index int) error { + err := arrayEach(value, func(value []byte, _ int) error { unquoted, err := unquoteSlice(value) if err != nil { return err } + inputDrvNames = append(inputDrvNames, unquoted) return nil @@ -171,11 +177,12 @@ func parseDerivation(derivationBytes []byte) (*Derivation, error) { }) case 2: // InputSources - err = arrayEach(value, func(value []byte, index int) error { + err = arrayEach(value, func(value []byte, _ int) error { unquoted, err := unquoteSlice(value) if err != nil { return err } + drv.InputSources = append(drv.InputSources, unquoted) return nil @@ -188,11 +195,12 @@ func parseDerivation(derivationBytes []byte) (*Derivation, error) { drv.Builder, err = unquoteSlice(value) case 5: // Arguments - err = arrayEach(value, func(value []byte, index int) error { + err = arrayEach(value, func(value []byte, _ int) error { unquoted, err := unquoteSlice(value) if err != nil { return err } + drv.Arguments = append(drv.Arguments, unquoted) return nil @@ -201,19 +209,21 @@ func parseDerivation(derivationBytes []byte) (*Derivation, error) { case 6: // Env drv.Env = make(map[string]string) prevEnvKey := "" - err = arrayEach(value, func(value []byte, index int) error { + err = arrayEach(value, func(value []byte, _ int) error { envValue := "" envKey := "" // For every field err := arrayEach(value, func(value []byte, index int) error { var err error + switch index { case 0: envKey, err = unquoteSlice(value) if err != nil { return err } + if envKey <= prevEnvKey { return fmt.Errorf("invalid env var order: %s <= %s", envKey, prevEnvKey) } diff --git a/pkg/derivation/store/badger.go b/pkg/derivation/store/badger.go index 901c064a..d8c699a8 100644 --- a/pkg/derivation/store/badger.go +++ b/pkg/derivation/store/badger.go @@ -107,6 +107,7 @@ func (bs *BadgerStore) Put(ctx context.Context, drv *derivation.Derivation) (str err = bs.db.Update(func(txn *badger.Txn) error { // store derivation itself drvEntry := badger.NewEntry([]byte("drv:"+drvPath), buf.Bytes()) + err := txn.SetEntry(drvEntry) if err != nil { return fmt.Errorf("unable to store derivation: %w", err) @@ -120,15 +121,14 @@ func (bs *BadgerStore) Put(ctx context.Context, drv *derivation.Derivation) (str // Store replacement string replacementEntry := badger.NewEntry([]byte("replacement:"+drvPath), []byte(drvReplacement)) - err = txn.SetEntry(replacementEntry) + err = txn.SetEntry(replacementEntry) if err != nil { return fmt.Errorf("unable to store replacement string: %w", err) } return nil }) - if err != nil { return "", err } @@ -175,6 +175,7 @@ func (bs *BadgerStore) Has(_ context.Context, derivationPath string) (bool, erro err := bs.db.View(func(txn *badger.Txn) error { opts := badger.DefaultIteratorOptions opts.PrefetchValues = false + it := txn.NewIterator(opts) defer it.Close() @@ -183,6 +184,7 @@ func (bs *BadgerStore) Has(_ context.Context, derivationPath string) (bool, erro for it.Seek(key); it.Valid(); it.Next() { item := it.Item() k := item.Key() + if bytes.Equal(k, key) { found = true diff --git a/pkg/derivation/store/store_test.go b/pkg/derivation/store/store_test.go index 95054556..24ade93a 100644 --- a/pkg/derivation/store/store_test.go +++ b/pkg/derivation/store/store_test.go @@ -21,7 +21,7 @@ var stores = []struct { }{ { Title: "MemoryStore", - NewStore: func(tmpDir string) derivation.Store { + NewStore: func(_ string) derivation.Store { return store.NewMapStore() }, }, { @@ -120,6 +120,7 @@ func TestStores(t *testing.T) { if err != nil { panic(err) } + assert.Equal(t, spExpected.Absolute(), drvPath) }) } diff --git a/pkg/hash/encode.go b/pkg/hash/encode.go index 340017dd..8288a82e 100644 --- a/pkg/hash/encode.go +++ b/pkg/hash/encode.go @@ -18,7 +18,7 @@ var hashtypeToNixHashString = map[int]string{ // Multihash returns the digest, in multihash format. func (h *Hash) Multihash() []byte { - d, _ := multihash.Encode(h.Digest(), uint64(h.HashType)) + d, _ := multihash.Encode(h.Digest(), uint64(h.HashType)) //nolint:gosec // "The error return is legacy; it is always nil." return d } diff --git a/pkg/hash/hash_test.go b/pkg/hash/hash_test.go index 7877de1f..fb9e7c30 100644 --- a/pkg/hash/hash_test.go +++ b/pkg/hash/hash_test.go @@ -12,6 +12,7 @@ func TestDigest(t *testing.T) { t.Run("valid sha256", func(t *testing.T) { nixString := "sha256:1rjs6c23nyf8zkmf7yxglz2q2m7v5kp51nc2m0lk4h998d0qiixs" sriString := "sha256-useIQUMpQTIpqILZUO4s+1SBxaev++Pq/Mh5OwQzWuY=" + h, err := hash.ParseNixBase32(nixString) if assert.NoError(t, err) { assert.Equal(t, mh.SHA2_256, h.HashType) @@ -32,6 +33,7 @@ func TestDigest(t *testing.T) { t.Run("valid sha512", func(t *testing.T) { nixString := "sha512:37iwwa5iw4m6pkd6qs2c5lw13q7y16hw2rv4i1cx6jax6yibhn6fgajbwc8p4j1fc6iicpy5r1vi7hpfq3n6z1ikhm5kcyz2b1frk80" //nolint:lll sriString := "sha512-AM3swhLfs1kqnDF8Ywd2F564Qy7+shgNc0GSixhfUj1nLFzRm66k6SxEsrPg0AR/8AicFiY0Nm1eUwmPRXEezw==" + h, err := hash.ParseNixBase32(nixString) if assert.NoError(t, err) { assert.Equal(t, mh.SHA2_512, h.HashType) diff --git a/pkg/hash/writer.go b/pkg/hash/writer.go index ec691792..49ee1747 100644 --- a/pkg/hash/writer.go +++ b/pkg/hash/writer.go @@ -8,7 +8,7 @@ import ( ) func New(hashType int) (*Hash, error) { - h, err := mh.GetHasher(uint64(hashType)) + h, err := mh.GetHasher(uint64(hashType)) //nolint:gosec if err != nil { return nil, err } @@ -38,7 +38,7 @@ func (h *Hash) Write(p []byte) (n int, err error) { return 0, fmt.Errorf("unable to write to hash function: %w", err) } - h.bytesWritten += uint64(n) + h.bytesWritten += uint64(n) //nolint:gosec return n, nil } diff --git a/pkg/hash/writer_test.go b/pkg/hash/writer_test.go index aed082ed..5383b0b8 100644 --- a/pkg/hash/writer_test.go +++ b/pkg/hash/writer_test.go @@ -31,6 +31,7 @@ func TestWriter(t *testing.T) { if err != nil { panic(err) } + assert.Equal(t, expectedDigest, h.Multihash()) }) @@ -62,6 +63,7 @@ func TestWriter(t *testing.T) { t.Run("reset", func(t *testing.T) { h.Reset() assert.Equal(t, uint64(0), h.BytesWritten()) + if assert.NoError(t, err, "calling sum shouldn't error") { assert.Equal(t, sha256DgstEmpty, h.Digest()) // calculate multihash diff --git a/pkg/nar/dump_test.go b/pkg/nar/dump_test.go index c004651e..4388f42b 100644 --- a/pkg/nar/dump_test.go +++ b/pkg/nar/dump_test.go @@ -46,6 +46,7 @@ func TestDumpPathOneByteRegular(t *testing.T) { if runtime.GOOS == "windows" { return } + tmpDir := t.TempDir() p := filepath.Join(tmpDir, "a") @@ -70,6 +71,7 @@ func TestDumpPathOneByteRegular(t *testing.T) { if err != nil { panic(err) } + assert.True(t, hdr.Executable, "regular should be executable") } }) @@ -174,7 +176,7 @@ func TestDumpPathFilter(t *testing.T) { var buf bytes.Buffer - err = nar.DumpPathFilter(&buf, tmpDir, func(name string, nodeType nar.NodeType) bool { + err = nar.DumpPathFilter(&buf, tmpDir, func(name string, _ nar.NodeType) bool { return name != p }) if assert.NoError(t, err) { diff --git a/pkg/nar/writer.go b/pkg/nar/writer.go index d807f26f..ff22a225 100644 --- a/pkg/nar/writer.go +++ b/pkg/nar/writer.go @@ -129,7 +129,7 @@ func (nw *Writer) emitNode(currentHeader *Header) (*Header, error) { return nil, err } - nw.contentWriter, err = wire.NewBytesWriter(nw.w, uint64(currentHeader.Size)) + nw.contentWriter, err = wire.NewBytesWriter(nw.w, uint64(currentHeader.Size)) //nolint:gosec if err != nil { return nil, err } diff --git a/pkg/narinfo/check.go b/pkg/narinfo/check.go index c9b31be8..ac4d250d 100644 --- a/pkg/narinfo/check.go +++ b/pkg/narinfo/check.go @@ -21,7 +21,6 @@ func (n *NarInfo) Check() error { for i, r := range n.References { _, err = storepath.FromString(r) - if err != nil { return fmt.Errorf("invalid Reference[%d]: %v", i, r) } diff --git a/pkg/narinfo/narinfo_test.go b/pkg/narinfo/narinfo_test.go index 2f44e5fe..265a2aa1 100644 --- a/pkg/narinfo/narinfo_test.go +++ b/pkg/narinfo/narinfo_test.go @@ -230,6 +230,7 @@ func BenchmarkNarInfo(b *testing.B) { defer f.Close() var buf bytes.Buffer + _, err = io.ReadAll(&buf) if err != nil { panic(err) diff --git a/pkg/narinfo/signature/util.go b/pkg/narinfo/signature/util.go index 28058afd..92d0f850 100644 --- a/pkg/narinfo/signature/util.go +++ b/pkg/narinfo/signature/util.go @@ -31,7 +31,6 @@ func decode(s string, dataSize int) (name string, data []byte, err error) { } data, err = base64.StdEncoding.DecodeString(dataStr) - if err != nil { return "", nil, fmt.Errorf("data is corrupt: %w", err) } diff --git a/pkg/nixbase32/nixbase32.go b/pkg/nixbase32/nixbase32.go index b7d7a73f..23464406 100644 --- a/pkg/nixbase32/nixbase32.go +++ b/pkg/nixbase32/nixbase32.go @@ -32,9 +32,9 @@ func Decode(dst, src []byte) (n int, err error) { maxDstSize := DecodedLen(len(src)) for n := 0; n < len(src); n++ { - b := uint64(n) * 5 - i := int(b / 8) - j := int(b % 8) + b := uint64(n) * 5 //nolint:gosec + i := int(b / 8) //nolint:gosec + j := int(b % 8) //nolint:gosec c := src[len(src)-n-1] digit := strings.IndexByte(Alphabet, c) @@ -63,9 +63,9 @@ func ValidateString(src string) error { maxDstSize := DecodedLen(len(src)) for n := 0; n < len(src); n++ { - b := uint64(n) * 5 - i := int(b / 8) - j := int(b % 8) + b := uint64(n) * 5 //nolint:gosec + i := int(b / 8) //nolint:gosec + j := int(b % 8) //nolint:gosec c := src[len(src)-n-1] digit := strings.IndexByte(Alphabet, c) @@ -107,8 +107,8 @@ func Encode(dst, src []byte) { for n = n - 1; n >= 0; n-- { b := uint64(n) * 5 - i := int(b / 8) - j := int(b % 8) + i := int(b / 8) //nolint:gosec + j := int(b % 8) //nolint:gosec c := src[i] >> j if i+1 < len(src) { @@ -129,8 +129,8 @@ func EncodeToString(src []byte) string { for n = n - 1; n >= 0; n-- { b := uint64(n) * 5 - i := int(b / 8) - j := int(b % 8) + i := int(b / 8) //nolint:gosec + j := int(b % 8) //nolint:gosec c := src[i] >> j if i+1 < len(src) { diff --git a/pkg/nixbase32/nixbase32_test.go b/pkg/nixbase32/nixbase32_test.go index 6339467d..a70f18cc 100644 --- a/pkg/nixbase32/nixbase32_test.go +++ b/pkg/nixbase32/nixbase32_test.go @@ -7,6 +7,7 @@ import ( "strings" "testing" + //nolint:revive . "github.com/nix-community/go-nix/pkg/nixbase32" ) @@ -151,6 +152,7 @@ func BenchmarkEncode(b *testing.B) { b.Run(strconv.Itoa(s), func(b *testing.B) { b.ReportAllocs() b.SetBytes(int64(len(bytes))) + for i := 0; i < b.N; i++ { Encode(buf, bytes) } @@ -168,6 +170,7 @@ func BenchmarkEncodeToString(b *testing.B) { b.Run(strconv.Itoa(s), func(b *testing.B) { b.ReportAllocs() b.SetBytes(int64(len(bytes))) + for i := 0; i < b.N; i++ { EncodeToString(bytes) } @@ -189,6 +192,7 @@ func BenchmarkDecode(b *testing.B) { b.Run(strconv.Itoa(s), func(b *testing.B) { b.ReportAllocs() b.SetBytes(int64(len(input))) + for i := 0; i < b.N; i++ { if _, err := Decode(bytes, input); err != nil { b.Fatal("error: %w", err) @@ -209,6 +213,7 @@ func BenchmarkDecodeString(b *testing.B) { b.Run(strconv.Itoa(s), func(b *testing.B) { b.ReportAllocs() b.SetBytes(int64(len(input))) + for i := 0; i < b.N; i++ { _, err := DecodeString(input) if err != nil { @@ -230,6 +235,7 @@ func BenchmarkValidateString(b *testing.B) { b.Run(strconv.Itoa(s), func(b *testing.B) { b.ReportAllocs() b.SetBytes(int64(len(input))) + for i := 0; i < b.N; i++ { err := ValidateString(input) if err != nil { diff --git a/pkg/wire/bytes_reader.go b/pkg/wire/bytes_reader.go index 6a27f297..4a2e8d4e 100644 --- a/pkg/wire/bytes_reader.go +++ b/pkg/wire/bytes_reader.go @@ -22,7 +22,7 @@ type BytesReader struct { func NewBytesReader(r io.Reader, contentLength uint64) *BytesReader { return &BytesReader{ contentLength: contentLength, - lr: io.LimitReader(r, int64(contentLength)), + lr: io.LimitReader(r, int64(contentLength)), //nolint:gosec r: r, } } diff --git a/pkg/wire/bytes_writer.go b/pkg/wire/bytes_writer.go index 16fe0f50..81d4ed2e 100644 --- a/pkg/wire/bytes_writer.go +++ b/pkg/wire/bytes_writer.go @@ -44,7 +44,7 @@ func (bw *BytesWriter) Write(p []byte) (n int, err error) { } bytesWritten, err := bw.w.Write(p) - bw.bytesWritten += uint64(bytesWritten) + bw.bytesWritten += uint64(bytesWritten) //nolint:gosec return bytesWritten, err } diff --git a/pkg/wire/read.go b/pkg/wire/read.go index 73b9997a..a5bc41f7 100644 --- a/pkg/wire/read.go +++ b/pkg/wire/read.go @@ -74,14 +74,14 @@ func ReadBytes(r io.Reader) (uint64, io.ReadCloser, error) { // A maximum number of bytes can be specified in max. // In the case of a packet exceeding the maximum number of bytes, // the reader won't seek to the end of the packet. -func ReadBytesFull(r io.Reader, max uint64) ([]byte, error) { +func ReadBytesFull(r io.Reader, maxBytes uint64) ([]byte, error) { contentLength, rd, err := ReadBytes(r) if err != nil { return []byte{}, err } - if contentLength > max { - return nil, fmt.Errorf("content length of %v bytes exceeds maximum of %v bytes", contentLength, max) + if contentLength > maxBytes { + return nil, fmt.Errorf("content length of %v bytes exceeds maximum of %v bytes", contentLength, maxBytes) } defer rd.Close() @@ -96,8 +96,8 @@ func ReadBytesFull(r io.Reader, max uint64) ([]byte, error) { } // ReadString reads a bytes packet and converts it to string. -func ReadString(r io.Reader, max uint64) (string, error) { - buf, err := ReadBytesFull(r, max) +func ReadString(r io.Reader, maxBytes uint64) (string, error) { + buf, err := ReadBytesFull(r, maxBytes) return string(buf), err }