Skip to content

Commit 8ac7754

Browse files
authored
Change from Set/GetNamedSecurityInfo to Nt{Query,Set}SecurityInfo (#1589)
* Update ACL grabbing API * Update ACL setting API * Fix import * Fix download case * Persist otherwise merged ACEs * Enable S2S and upload; add proper comparison * Fix double-import * Only sanity-check when necessary * Address comments
1 parent ee7a994 commit 8ac7754

11 files changed

+410
-156
lines changed

e2etest/declarativeScenario.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import (
2727
"os"
2828
"path"
2929
"path/filepath"
30-
"strings"
3130
"time"
3231

3332
"github.com/Azure/azure-storage-azcopy/v10/common"
@@ -393,7 +392,7 @@ func (s *scenario) validateSMBPermissionsByValue(expected, actual string, objNam
393392
actualSDDL, err := sddl.ParseSDDL(actual)
394393
s.a.AssertNoErr(err)
395394

396-
s.a.Assert(strings.TrimSuffix(actualSDDL.PortableString(), "S:NO_ACCESS_CONTROL"), equals(), strings.TrimSuffix(expectedSDDL.PortableString(), "S:NO_ACCESS_CONTROL"), "On object "+objName)
395+
s.a.Assert(expectedSDDL.Compare(actualSDDL), equals(), true)
397396
}
398397

399398
func (s *scenario) validateContent() {

e2etest/scenario_helpers.go

+44
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"time"
3737

3838
"github.com/Azure/azure-storage-azcopy/v10/azbfs"
39+
"github.com/Azure/azure-storage-azcopy/v10/sddl"
3940
"github.com/minio/minio-go"
4041

4142
"github.com/Azure/azure-storage-azcopy/v10/common"
@@ -684,6 +685,19 @@ func (scenarioHelper) generateAzureFilesFromList(c asserter, options *generateAz
684685
if f.creationProperties.smbPermissionsSddl != nil || f.creationProperties.smbAttributes != nil {
685686
_, err := dir.SetProperties(ctx, ad.toHeaders(c, options.shareURL).SMBProperties)
686687
c.AssertNoErr(err)
688+
689+
if f.creationProperties.smbPermissionsSddl != nil {
690+
prop, err := dir.GetProperties(ctx)
691+
c.AssertNoErr(err)
692+
693+
perm, err := options.shareURL.GetPermission(ctx, prop.FilePermissionKey())
694+
c.AssertNoErr(err)
695+
696+
dest, _ := sddl.ParseSDDL(perm.Permission)
697+
source, _ := sddl.ParseSDDL(*f.creationProperties.smbPermissionsSddl)
698+
699+
c.Assert(dest.Compare(source), equals(), true)
700+
}
687701
}
688702

689703
// set other properties
@@ -721,6 +735,36 @@ func (scenarioHelper) generateAzureFilesFromList(c asserter, options *generateAz
721735
c.AssertNoErr(err)
722736
c.Assert(cResp.StatusCode(), equals(), 201)
723737

738+
if f.creationProperties.smbPermissionsSddl != nil || f.creationProperties.smbAttributes != nil {
739+
/*
740+
via Jason Shay:
741+
Providing securityKey/SDDL during 'PUT File' and 'PUT Properties' can and will provide different results/semantics.
742+
This is true for the REST PUT commands, as well as locally when providing a SECURITY_DESCRIPTOR in the SECURITY_ATTRIBUTES structure in the CreateFile() call.
743+
In both cases of file creation (CreateFile() and REST PUT File), the actual security descriptor applied to the file can undergo some changes as compared to the input.
744+
745+
SetProperties() (and NtSetSecurityObject) use update semantics, so it should store what you provide it (with a couple exceptions).
746+
And on the cloud share, you would need 'Set Properties' to be called as a final step, to save the final ACLs with 'update' semantics.
747+
748+
749+
*/
750+
751+
_, err := file.SetHTTPHeaders(ctx, headers)
752+
c.AssertNoErr(err)
753+
754+
if f.creationProperties.smbPermissionsSddl != nil {
755+
prop, err := file.GetProperties(ctx)
756+
c.AssertNoErr(err)
757+
758+
perm, err := options.shareURL.GetPermission(ctx, prop.FilePermissionKey())
759+
c.AssertNoErr(err)
760+
761+
dest, _ := sddl.ParseSDDL(perm.Permission)
762+
source, _ := sddl.ParseSDDL(*f.creationProperties.smbPermissionsSddl)
763+
764+
c.Assert(dest.Compare(source), equals(), true)
765+
}
766+
}
767+
724768
_, err = file.UploadRange(context.Background(), 0, contentR, nil)
725769
if err == nil {
726770
c.Failed()

e2etest/scenario_os_helpers_for_windows.go

+22-1
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@ import (
2828
"strings"
2929
"syscall"
3030
"time"
31+
"unsafe"
3132

3233
"github.com/Azure/azure-storage-azcopy/v10/common"
3334
"github.com/Azure/azure-storage-azcopy/v10/ste"
35+
"github.com/hillu/go-ntdll"
3436
"golang.org/x/sys/windows"
3537
)
3638

@@ -86,8 +88,27 @@ func (osScenarioHelper) getFileAttrs(c asserter, filepath string) *uint32 {
8688
}
8789

8890
func (osScenarioHelper) getFileSDDLString(c asserter, filepath string) *string {
89-
sd, err := windows.GetNamedSecurityInfo(filepath, windows.SE_FILE_OBJECT, windows.OWNER_SECURITY_INFORMATION|windows.GROUP_SECURITY_INFORMATION|windows.DACL_SECURITY_INFORMATION)
91+
srcPtr, err := syscall.UTF16PtrFromString(filepath)
9092
c.AssertNoErr(err)
93+
// custom open call, because must specify FILE_FLAG_BACKUP_SEMANTICS to make --backup mode work properly (i.e. our use of SeBackupPrivilege)
94+
fd, err := windows.CreateFile(srcPtr,
95+
windows.GENERIC_READ, windows.FILE_SHARE_READ, nil,
96+
windows.OPEN_EXISTING, windows.FILE_FLAG_BACKUP_SEMANTICS, 0)
97+
c.AssertNoErr(err)
98+
99+
buf := make([]byte, 512)
100+
bufLen := uint32(len(buf))
101+
status := ntdll.CallWithExpandingBuffer(func() ntdll.NtStatus {
102+
return ntdll.NtQuerySecurityObject(
103+
ntdll.Handle(fd),
104+
windows.OWNER_SECURITY_INFORMATION|windows.GROUP_SECURITY_INFORMATION|windows.DACL_SECURITY_INFORMATION,
105+
(*ntdll.SecurityDescriptor)(unsafe.Pointer(&buf[0])),
106+
uint32(len(buf)),
107+
&bufLen)
108+
}, &buf, &bufLen)
109+
110+
c.Assert(status, equals(), ntdll.STATUS_SUCCESS)
111+
sd := (*windows.SECURITY_DESCRIPTOR)(unsafe.Pointer(&buf[0])) // ntdll.SecurityDescriptor is equivalent
91112
ret := sd.String()
92113

93114
return &ret

e2etest/zt_preserve_smb_properties_test.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
"golang.org/x/sys/windows"
1313
)
1414

15-
const SampleSDDL = "O:<placeholder>G:<placeholder>D:AI(A;ID;FA;;;SY)(A;ID;FA;;;BA)(A;ID;FA;;;<placeholder>)S:NO_ACCESS_CONTROL"
15+
const SampleSDDL = "O:<placeholder>G:<placeholder>D:AI(A;ID;FA;;;SY)(A;ID;FA;;;BA)(A;ID;FA;;;<placeholder>)(D;;FX;;;SY)S:NO_ACCESS_CONTROL"
1616
const RootSampleSDDL = "O:<placeholder>G:<placeholder>D:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;FA;;;<placeholder>)S:NO_ACCESS_CONTROL"
1717
const FolderSampleSDDL = "O:<placeholder>G:<placeholder>D:AI(A;OICIID;FA;;;SY)(A;OICIID;FA;;;BA)(A;OICIID;FA;;;<placeholder>)S:NO_ACCESS_CONTROL"
1818
const SampleSDDLPlaceHolder = "<placeholder>"
@@ -63,7 +63,8 @@ func TestProperties_SMBPermissionsSDDLPreserved(t *testing.T) {
6363

6464
RunScenarios(t, eOperation.Copy(), eTestFromTo.Other(
6565
common.EFromTo.LocalFile(),
66-
// common.EFromTo.FileFile(), // TODO: finish inquiring with Jason Shay about this wonkiness. Context: Auto-inherit bit is getting flipped on S2S unrelated to azcopy
66+
common.EFromTo.FileLocal(),
67+
common.EFromTo.FileFile(),
6768
), eValidate.Auto(), params{
6869
recursive: true,
6970
preserveSMBInfo: true,

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ require (
1414
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
1515
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
1616
github.com/google/uuid v1.3.0
17-
github.com/hillu/go-ntdll v0.0.0-20211217090901-0dd7663bb804
17+
github.com/hillu/go-ntdll v0.0.0-20211013113251-9d1dd10a143d
1818
github.com/kr/pretty v0.3.0 // indirect
1919
github.com/mattn/go-ieproxy v0.0.1
2020
github.com/minio/minio-go v6.0.14+incompatible

go.sum

+5-5
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,12 @@ github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3
8080
github.com/JeffreyRichter/enum v0.0.0-20180725232043-2567042f9cda h1:NOo6+gM9NNPJ3W56nxOKb4164LEw094U0C8zYQM8mQU=
8181
github.com/JeffreyRichter/enum v0.0.0-20180725232043-2567042f9cda/go.mod h1:2CaSFTh2ph9ymS6goiOKIBdfhwWUVsX4nQ5QjIYFHHs=
8282
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
83-
github.com/PuerkitoBio/goquery v1.7.1/go.mod h1:XY0pP4kfraEmmV1O7Uf6XyjoslwsneBbgeDjLYuN8xY=
83+
github.com/PuerkitoBio/goquery v1.6.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
8484
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
8585
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
8686
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
8787
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
88-
github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY=
88+
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
8989
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
9090
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
9191
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@@ -271,8 +271,8 @@ github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn
271271
github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
272272
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
273273
github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
274-
github.com/hillu/go-ntdll v0.0.0-20211217090901-0dd7663bb804 h1:OndflF5d6GVByvvo/NKqPtS71SAREkxzE1eYGa2QZ98=
275-
github.com/hillu/go-ntdll v0.0.0-20211217090901-0dd7663bb804/go.mod h1:cHjYsnAnSckPDx8/H01Y+owD1hf2adLA6VRiw4guEbA=
274+
github.com/hillu/go-ntdll v0.0.0-20211013113251-9d1dd10a143d h1:GS2pWoXQIkKRhQqfy9QKy/aa928aUNEOIQkGxPivCAE=
275+
github.com/hillu/go-ntdll v0.0.0-20211013113251-9d1dd10a143d/go.mod h1:jsDwCq+VLI6qnVqFRAlfOC14FULjT1lt99vnszAZ6/Q=
276276
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
277277
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
278278
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@@ -508,7 +508,6 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
508508
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
509509
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
510510
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
511-
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
512511
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
513512
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
514513
golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 h1:+6WJMRLHlD7X7frgp7TUZ36RnQzSf9wVVTNakEp+nqY=
@@ -588,6 +587,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
588587
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
589588
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
590589
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
590+
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
591591
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
592592
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
593593
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

sddl/parseSddl.go

+163
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
// Copyright © Microsoft <wastore@microsoft.com>
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy
4+
// of this software and associated documentation files (the "Software"), to deal
5+
// in the Software without restriction, including without limitation the rights
6+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
// copies of the Software, and to permit persons to whom the Software is
8+
// furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
// THE SOFTWARE.
20+
21+
package sddl
22+
23+
import (
24+
"errors"
25+
"fmt"
26+
"regexp"
27+
"strings"
28+
)
29+
30+
var translateSID = OSTranslateSID // this layer of indirection is to support unit testing. TODO: it's ugly to set a global to test. Do something better one day
31+
32+
func IffInt(condition bool, tVal, fVal int) int {
33+
if condition {
34+
return tVal
35+
}
36+
return fVal
37+
}
38+
39+
func ParseSDDL(input string) (sddl SDDLString, err error) {
40+
scope := 0 // if scope is 1, we're in an ACE string, if scope is 2, we're in a resource attribute.
41+
inString := false // If a quotation mark was found, we've entered a string and should ignore all characters except another quotation mark.
42+
elementStart := make([]int, 0) // This is the start of the element we're currently analyzing. If the array has more than one element, we're probably under a lower scope.
43+
awaitingACLFlags := false // If this is true, a ACL section was just entered, and we're awaiting our first ACE string
44+
var elementType rune // We need to keep track of which section of the SDDL string we're in.
45+
for k, v := range input {
46+
switch {
47+
case inString: // ignore characters within a string-- except for the end of a string, and escaped quotes
48+
if v == '"' && input[k-1] != '\\' {
49+
inString = false
50+
}
51+
case v == '"':
52+
inString = true
53+
case v == '(': // this comes before scope == 1 because ACE strings can be multi-leveled. We only care about the bottom level.
54+
scope++
55+
if scope == 1 { // only do this if we're in the base of an ACE string-- We don't care about the metadata as much.
56+
if awaitingACLFlags {
57+
err := sddl.setACLFlags(input[elementStart[0]:k], elementType)
58+
59+
if err != nil {
60+
return sddl, err
61+
}
62+
63+
awaitingACLFlags = false
64+
}
65+
elementStart = append(elementStart, k+1) // raise the element start scope
66+
err := sddl.startACL(elementType)
67+
68+
if err != nil {
69+
return sddl, err
70+
}
71+
}
72+
case v == ')':
73+
// (...,...,...,(...))
74+
scope--
75+
if scope == 0 {
76+
err := sddl.putACLElement(input[elementStart[1]:k], elementType)
77+
78+
if err != nil {
79+
return sddl, err
80+
}
81+
82+
elementStart = elementStart[:1] // lower the element start scope
83+
}
84+
case scope == 1: // We're at the top level of an ACE string
85+
switch v {
86+
case ';':
87+
// moving to the next element
88+
err := sddl.putACLElement(input[elementStart[1]:k], elementType)
89+
90+
if err != nil {
91+
return sddl, err
92+
}
93+
94+
elementStart[1] = k + 1 // move onto the next bit of the element scope
95+
}
96+
case scope == 0: // We're at the top level of a SDDL string
97+
if k == len(input)-1 || v == ':' { // If we end the string OR start a new section
98+
if elementType != 0x00 {
99+
switch elementType {
100+
case 'O':
101+
// you are here:
102+
// V
103+
// O:...G:
104+
// ^
105+
// k-1
106+
// string separations in go happen [x:y).
107+
sddl.OwnerSID = strings.TrimSpace(input[elementStart[0]:IffInt(k == len(input)-1, len(input), k-1)])
108+
case 'G':
109+
sddl.GroupSID = strings.TrimSpace(input[elementStart[0]:IffInt(k == len(input)-1, len(input), k-1)])
110+
case 'D', 'S': // These are both parsed WHILE they happen, UNLESS we're awaiting flags.
111+
if awaitingACLFlags {
112+
err := sddl.setACLFlags(strings.TrimSpace(input[elementStart[0]:IffInt(k == len(input)-1, len(input), k-1)]), elementType)
113+
114+
if err != nil {
115+
return sddl, err
116+
}
117+
}
118+
default:
119+
return sddl, fmt.Errorf("%s is an invalid SDDL section", string(elementType))
120+
}
121+
}
122+
123+
if v == ':' {
124+
// set element type to last character
125+
elementType = rune(input[k-1])
126+
127+
// await ACL flags
128+
if elementType == 'D' || elementType == 'S' {
129+
awaitingACLFlags = true
130+
}
131+
132+
// set element start to next character
133+
if len(elementStart) == 0 { // start the list if it's empty
134+
elementStart = append(elementStart, k+1)
135+
} else if len(elementStart) > 1 {
136+
return sddl, errors.New("elementStart too long for starting a new part of a SDDL")
137+
} else { // assign the new element start
138+
elementStart[0] = k + 1
139+
}
140+
}
141+
}
142+
}
143+
}
144+
145+
if scope > 0 || inString {
146+
return sddl, errors.New("string or scope not fully exited")
147+
}
148+
149+
if err == nil {
150+
if !sanityCheckSDDLParse(input, sddl) {
151+
return sddl, errors.New("SDDL parsing sanity check failed")
152+
}
153+
}
154+
155+
return
156+
}
157+
158+
var sddlWhitespaceRegex = regexp.MustCompile(`[\x09-\x0D ]`)
159+
160+
func sanityCheckSDDLParse(original string, parsed SDDLString) bool {
161+
return sddlWhitespaceRegex.ReplaceAllString(original, "") ==
162+
sddlWhitespaceRegex.ReplaceAllString(parsed.String(), "")
163+
}
File renamed without changes.

0 commit comments

Comments
 (0)