Skip to content
Open
Show file tree
Hide file tree
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
15 changes: 15 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@

# Change Log

## Version 10.32.3

### Dependency updates
1. golang.org/x/crypto v0.47.0 -> v0.49.0
2. golang.org/x/sync v0.19.0 -> v0.20.0
3. golang.org/x/sys v0.40.0 -> v0.42.0
4. golang.org/x/net v0.49.0 -> v0.52.0
5. golang.org/x/text v0.33.0 -> v0.35.0
6. google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 -> v0.0.0-20260319201613-d00831a3d3e7
7. google.golang.org/grpc v1.79.1 -> v1.79.3

### Vulnerability fixes
1. Fixed an issue where a maliciously crafted SDDL could crash AzCopy on Linux.


## Version 10.32.2

### Dependency updates
Expand Down
2 changes: 2 additions & 0 deletions build-1es-pipeline.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ extends:
# For productions pipelines, use "Official".
template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines
parameters:
featureFlags:
disableNetworkIsolation: true
sdl:
sourceAnalysisPool:
name: azcopy-pool # Name of your hosted pool
Expand Down
2 changes: 1 addition & 1 deletion common/version.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package common

const AzcopyVersion = "10.32.2"
const AzcopyVersion = "10.32.3"
const UserAgent = "AzCopy/" + AzcopyVersion
const S3ImportUserAgent = "S3Import " + UserAgent
const GCPImportUserAgent = "GCPImport " + UserAgent
Expand Down
14 changes: 7 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ require (
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/spf13/cobra v1.8.1
github.com/wastore/keyctl v0.3.1
golang.org/x/crypto v0.47.0 // indirect
golang.org/x/crypto v0.49.0 // indirect
golang.org/x/oauth2 v0.34.0
golang.org/x/sync v0.19.0
golang.org/x/sys v0.40.0
golang.org/x/sync v0.20.0
golang.org/x/sys v0.42.0
google.golang.org/api v0.249.0
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
)
Expand All @@ -35,7 +35,7 @@ require (
github.com/Azure/go-autorest/autorest/date v0.3.0
github.com/keybase/go-keychain v0.0.1
github.com/spf13/pflag v1.0.5
golang.org/x/net v0.49.0
golang.org/x/net v0.52.0
)

require (
Expand Down Expand Up @@ -87,12 +87,12 @@ require (
go.opentelemetry.io/otel/sdk v1.40.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect
go.opentelemetry.io/otel/trace v1.40.0 // indirect
golang.org/x/text v0.33.0 // indirect
golang.org/x/text v0.35.0 // indirect
golang.org/x/time v0.13.0 // indirect
google.golang.org/genproto v0.0.0-20250922171735-9219d122eba9 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect
google.golang.org/grpc v1.79.1 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 // indirect
google.golang.org/grpc v1.79.3 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Expand Down
14 changes: 14 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -181,24 +181,34 @@ go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZY
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI=
golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
Expand All @@ -212,8 +222,12 @@ google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 h1:ndE4FoJqsIceKP2oYSnUZqhTdYufCYYkqwtFzfrhI7w=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
42 changes: 28 additions & 14 deletions sddl/sddlHelper_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ package sddl
import (
"encoding/binary"
"fmt"
"math"
"strconv"
"unsafe"

Expand Down Expand Up @@ -589,6 +590,14 @@ var domainRidShortcuts = map[string]uint32{

/****************************************************************************/

// isRangeValid validates that offset+length does not overflow, and lies under descriptorLength
func isRangeValid(offset, length, descriptorLength uint32) bool {
u64RangeEnd := uint64(offset) + uint64(length)

return u64RangeEnd <= math.MaxUint32 && // the range is valid, provided we do not overflow
u64RangeEnd <= uint64(descriptorLength) // and that we line up with the max length of the descriptor.
}

// Test whether sd refers to a valid Security Descriptor.
// We do some basic validations of the SECURITY_DESCRIPTOR_RELATIVE header.
// 'flags' is used to convey what all information does the caller want us to verify in the binary SD.
Expand All @@ -605,6 +614,7 @@ func sdRelativeIsValid(sd []byte, flags SECURITY_INFORMATION) error {
offsetGroup := binary.LittleEndian.Uint32(sd[8:12])
offsetSacl := binary.LittleEndian.Uint32(sd[12:16])
offsetDacl := binary.LittleEndian.Uint32(sd[16:20])
sdLenU32 := uint32(len(sd))

// Now validate sanity of these fields.
if revision != SDDL_REVISION {
Expand All @@ -629,8 +639,9 @@ func sdRelativeIsValid(sd []byte, flags SECURITY_INFORMATION) error {

// offsetDacl may be 0 which would mean "No ACLs" aka "allow all users".
// If non-zero, OffsetDacl must point inside the relative Security Descriptor.
if offsetDacl != 0 && offsetDacl+uint32(unsafe.Sizeof(ACL{})) > uint32(len(sd)) {
return fmt.Errorf("DACL (offsetDacl=%d) must lie within sd (length=%d)", offsetDacl, len(sd))
if offsetDacl != 0 &&
!isRangeValid(offsetDacl, uint32(unsafe.Sizeof(ACL{})), sdLenU32) {
return fmt.Errorf("DACL (offsetDacl=%d, end=%d) must lie within sd (length=%d)", offsetDacl, uint64(offsetDacl)+uint64(unsafe.Sizeof(ACL{})), len(sd))
}
}

Expand All @@ -639,8 +650,8 @@ func sdRelativeIsValid(sd []byte, flags SECURITY_INFORMATION) error {
// SE_SACL_PRESENT bit is optional. If not set it means there is no SACL present.
if (control&SE_SACL_PRESENT) != 0 && offsetSacl != 0 {
// OffsetSacl must point inside the relative Security Descriptor.
if offsetSacl+uint32(unsafe.Sizeof(ACL{})) > uint32(len(sd)) {
return fmt.Errorf("SACL (offsetSacl=%d) must lie within sd (length=%d)", offsetSacl, len(sd))
if !isRangeValid(offsetSacl, uint32(unsafe.Sizeof(ACL{})), sdLenU32) {
return fmt.Errorf("SACL (offsetSacl=%d, end=%d) must lie within sd (length=%d)", offsetSacl, uint64(offsetSacl)+uint64(unsafe.Sizeof(ACL{})), len(sd))
}
}
}
Expand All @@ -652,9 +663,9 @@ func sdRelativeIsValid(sd []byte, flags SECURITY_INFORMATION) error {
}

// OffsetOwner must point inside the relative Security Descriptor.
if offsetOwner+uint32(unsafe.Sizeof(SID{})) > uint32(len(sd)) {
return fmt.Errorf("OwnerSID (offsetOwner=%d) must lie within sd (length=%d)",
offsetOwner, len(sd))
if !isRangeValid(offsetOwner, uint32(unsafe.Sizeof(SID{})), sdLenU32) {
return fmt.Errorf("OwnerSID (offsetOwner=%d, end=%d) must lie within sd (length=%d)",
offsetOwner, uint64(offsetOwner)+uint64(unsafe.Sizeof(SID{})), len(sd))
}
}

Expand All @@ -665,9 +676,9 @@ func sdRelativeIsValid(sd []byte, flags SECURITY_INFORMATION) error {
}

// OffsetGroup must point inside the relative Security Descriptor.
if offsetGroup+uint32(unsafe.Sizeof(SID{})) > uint32(len(sd)) {
return fmt.Errorf("GroupSID (offsetGroup=%d) must lie within sd (length=%d)",
offsetGroup, len(sd))
if !isRangeValid(offsetGroup, uint32(unsafe.Sizeof(SID{})), sdLenU32) {
return fmt.Errorf("GroupSID (offsetGroup=%d, end=%d) must lie within sd (length=%d)",
offsetGroup, uint64(offsetGroup)+uint64(unsafe.Sizeof(SID{})), len(sd))
}
}

Expand Down Expand Up @@ -873,6 +884,7 @@ func aceRightsToString(aceRights uint32) string {

// Does the aceType correspond to an object ACE?
// We don't support object ACEs.
//
//nolint:deadcode,unused
func isObjectAce(aceType byte) bool {
switch aceType {
Expand Down Expand Up @@ -1017,6 +1029,7 @@ func getOwnerSidString(sd []byte) (string, error) {

// SECURITY_DESCRIPTOR_RELATIVE.OffsetOwner.
offsetOwner := binary.LittleEndian.Uint32(sd[4:8])
// we don't care as much about validating the range here -- sidToString ensures there's at least 8 bytes after slicing.
if offsetOwner >= uint32(len(sd)) {
return "", fmt.Errorf("offsetOwner (%d) points outside Security Descriptor of size %d bytes!",
offsetOwner, len(sd))
Expand Down Expand Up @@ -1044,6 +1057,7 @@ func getGroupSidString(sd []byte) (string, error) {

// SECURITY_DESCRIPTOR_RELATIVE.OffsetGroup.
offsetGroup := binary.LittleEndian.Uint32(sd[8:12])
// we don't care as much about validating the range here -- sidToString ensures there's at least 8 bytes after slicing.
if offsetGroup >= uint32(len(sd)) {
return "", fmt.Errorf("offsetGroup (%d) points outside Security Descriptor of size %d bytes!",
offsetGroup, len(sd))
Expand Down Expand Up @@ -1111,7 +1125,7 @@ func getDaclString(sd []byte) (string, error) {
return daclString, nil
}

if (dacloffset + 8) > uint32(len(sd)) {
if !isRangeValid(dacloffset, 8, uint32(len(sd))) {
return "", fmt.Errorf("dacloffset (%d) points outside Security Descriptor of size %d bytes!",
dacloffset+8, len(sd))
}
Expand Down Expand Up @@ -1141,16 +1155,16 @@ func getDaclString(sd []byte) (string, error) {
// If numAces is 0 it'll result in daclString to have only flags and no ACEs.
// Such an ACL will mean "allow nobody".
for i := 0; i < int(numAces); i++ {
if (offset + 4) > uint32(len(sd)) {
if !isRangeValid(offset, 4, uint32(len(sd))) {
return "", fmt.Errorf("Short ACE (offset=%d), Security Descriptor size=%d bytes!",
offset, len(sd))
}

// ACCESS_ALLOWED_ACE.Header.AceSize.
ace_size := uint32(binary.LittleEndian.Uint16(sd[offset+2 : offset+4]))

if (offset + ace_size) > uint32(len(sd)) {
return "", fmt.Errorf("ACE (offset=%d, ace_size=%d) lies outside Security Descriptor of size %d bytes!", offset, ace_size, len(sd))
if !isRangeValid(offset, ace_size, uint32(len(sd))) {
return "", fmt.Errorf("ACE (offset=%d, ace_size=%d) lies outside Security Descriptor of size %d bytes (or exceeds uint32 limits)!", offset, ace_size, len(sd))
}

aceStr, err := aceToString(sd[offset : offset+ace_size])
Expand Down
103 changes: 103 additions & 0 deletions sddl/sddlHelper_linux_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//go:build linux
// +build linux

package sddl

import (
"math"
"testing"
"unsafe"

"github.com/stretchr/testify/assert"
)

func TestMaliciousRelativeSDDLCrashPrevented(t *testing.T) {
a := assert.New(t)

craftedDescriptor := SECURITY_DESCRIPTOR_RELATIVE{
Revision: SDDL_REVISION,
Sbz1: 0, // must be zero
Control: SE_SELF_RELATIVE,
Data: [0]BYTE{},
}

type testTargets struct {
testName string
offset *DWORD
offsetValue DWORD
controlFlag SECURITY_DESCRIPTOR_CONTROL
siFlags SECURITY_INFORMATION
}

maliciousOffsetSID := DWORD(math.MaxUint32 - unsafe.Sizeof(SID{}) + 1)
maliciousOffsetACL := DWORD(math.MaxUint32 - unsafe.Sizeof(ACL{}) + 1)

fieldsToTest := []testTargets{
{"DACL", &craftedDescriptor.OffsetDacl, maliciousOffsetACL, SE_DACL_PRESENT | SE_SELF_RELATIVE, DACL_SECURITY_INFORMATION},
{"SACL", &craftedDescriptor.OffsetSacl, maliciousOffsetACL, SE_SACL_PRESENT | SE_SELF_RELATIVE, SACL_SECURITY_INFORMATION},
{"Owner", &craftedDescriptor.OffsetOwner, maliciousOffsetSID, SE_SELF_RELATIVE, OWNER_SECURITY_INFORMATION},
{"Group", &craftedDescriptor.OffsetGroup, maliciousOffsetSID, SE_SELF_RELATIVE, GROUP_SECURITY_INFORMATION},
}

//sdRelativeIsValid()
for _, v := range fieldsToTest {
// take note of the original values of the fields we're updating.
originalOffset := *v.offset
originalControl := craftedDescriptor.Control

// Update our offset and control bits.
*v.offset = v.offsetValue
craftedDescriptor.Control = v.controlFlag

// Run sdRelativeIsValid inside a closure
// so that we can validate that it did not crash, and errored appropriately.
panicked, err := func() (panicked any, err error) {
defer func() {
panicked = recover()
}()

// Goland thinks these byte typecasts are redundant. They are not!
//goland:noinspection GoRedundantConversion
descData := ([]byte)(unsafe.Slice((*byte)(unsafe.Pointer(&craftedDescriptor)), unsafe.Sizeof(craftedDescriptor)))

err = sdRelativeIsValid(descData, v.siFlags)

return
}()

// reset our flags
*v.offset = originalOffset
craftedDescriptor.Control = originalControl

// We should not have panicked, just errored cleanly.
a.Nil(panicked, v.testName, "panicked")
a.NotNil(err, v.testName, "should have returned an error")
a.Contains(err.Error(), "must lie within sd", v.testName, "error wasn't as expected")
}

// let's test one more thing. getDaclString could cause issues in the past, let's set the malicious data for that and make sure we error.
craftedDescriptor = SECURITY_DESCRIPTOR_RELATIVE{
Revision: SDDL_REVISION,
Sbz1: 0, // must be zero
Control: SE_SELF_RELATIVE | SE_DACL_PRESENT,
OffsetDacl: maliciousOffsetACL,
Data: [0]BYTE{},
}

panicked, err := func() (panicked any, err error) {
defer func() {
panicked = recover()
}()

// Goland thinks these byte typecasts are redundant. They are not!
//goland:noinspection GoRedundantConversion
descData := ([]byte)(unsafe.Slice((*byte)(unsafe.Pointer(&craftedDescriptor)), unsafe.Sizeof(craftedDescriptor)))

_, err = getDaclString(descData)
return
}()

a.Nil(panicked, "getDaclString panicked")
a.NotNil(err, "getDaclString should error")
a.Contains(err.Error(), "points outside Security Descriptor of size", "getDaclString error wasn't what was expected")
}
9 changes: 5 additions & 4 deletions ste/sourceInfoProvider-Local_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package ste

import (
"errors"
"fmt"
"os/user"
"strconv"
Expand Down Expand Up @@ -57,7 +58,7 @@ func (f localFileSourceInfoProvider) GetUNIXProperties() (common.UnixStatAdapter
err = unix.Stat(f.transferInfo.Source, &stat)
}
if err != nil {
return nil, err
return nil, err
}

return StatTAdapter(stat), nil
Expand Down Expand Up @@ -206,8 +207,8 @@ func (f localFileSourceInfoProvider) GetSDDL() (string, error) {
// This is the Windows equivalent of ConvertSecurityDescriptorToStringSecurityDescriptorW().
sdStr, err := sddl.SecurityDescriptorToString(sd)
if err != nil {
// Panic, as it's unexpected and we would want to know.
panic(fmt.Errorf("Cannot parse binary Security Descriptor returned by QuerySecurityObject(%s, 0x%x): %v", f.jptm.Info().Source, securityInfoFlags, err))
// No longer panic here. A malformed SD would prevent the entire job from running.
return "", fmt.Errorf("cannot parse binary Security Descriptor returned by QuerySecurityObject(%s, 0x%x): %w", f.jptm.Info().Source, securityInfoFlags, err)
}

fSDDL, err := sddl.ParseSDDL(sdStr)
Expand All @@ -216,7 +217,7 @@ func (f localFileSourceInfoProvider) GetSDDL() (string, error) {
}

if strings.TrimSpace(fSDDL.String()) != strings.TrimSpace(sdStr) {
panic("SDDL sanity check failed (parsed string output != original string)")
return "", errors.New("SDDL sanity check failed (parsed string output != original string)")
}

return fSDDL.PortableString(), nil
Expand Down
Loading