From 86c19346bb4426525095524280680e73d234371a Mon Sep 17 00:00:00 2001
From: Volker Schukai <volker.schukai@schukai.com>
Date: Sun, 7 Jul 2024 14:46:19 +0200
Subject: [PATCH] fix: sync with keep now works with missing nodes

---
 source/go.mod                                 |   6 +-
 source/go.sum                                 |   6 ++
 source/html/sync.go                           | 101 +++++++++++++-----
 .../golang.org/x/crypto/bcrypt/bcrypt.go      |   2 +-
 .../golang.org/x/crypto/blowfish/cipher.go    |   2 +-
 source/vendor/golang.org/x/sys/unix/mremap.go |   5 +
 .../golang.org/x/sys/unix/syscall_darwin.go   |  12 +++
 .../golang.org/x/sys/unix/syscall_unix.go     |   9 ++
 .../x/sys/unix/zsyscall_darwin_amd64.go       |  33 ++++++
 .../x/sys/unix/zsyscall_darwin_amd64.s        |  10 ++
 .../x/sys/unix/zsyscall_darwin_arm64.go       |  33 ++++++
 .../x/sys/unix/zsyscall_darwin_arm64.s        |  10 ++
 .../x/sys/windows/security_windows.go         |  24 ++++-
 .../x/sys/windows/zsyscall_windows.go         |   9 ++
 source/vendor/modules.txt                     |   8 +-
 15 files changed, 236 insertions(+), 34 deletions(-)

diff --git a/source/go.mod b/source/go.mod
index fc124dd..bb423c5 100644
--- a/source/go.mod
+++ b/source/go.mod
@@ -8,8 +8,8 @@ require (
 	github.com/tdewolff/parse/v2 v2.7.15
 	gitlab.schukai.com/oss/libraries/go/application/xflags v1.16.2
 	gitlab.schukai.com/oss/libraries/go/markup/html v0.4.2
-	golang.org/x/crypto v0.24.0
-	golang.org/x/net v0.26.0
+	golang.org/x/crypto v0.25.0
+	golang.org/x/net v0.27.0
 	gopkg.in/yaml.v3 v3.0.1
 )
 
@@ -28,6 +28,6 @@ require (
 	gitlab.schukai.com/oss/libraries/go/utilities/data.git v0.2.0 // indirect
 	gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.9.2 // indirect
 	golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect
-	golang.org/x/sys v0.21.0 // indirect
+	golang.org/x/sys v0.22.0 // indirect
 	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
 )
diff --git a/source/go.sum b/source/go.sum
index 7bc3259..0e6076f 100644
--- a/source/go.sum
+++ b/source/go.sum
@@ -62,6 +62,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
 golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
+golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
+golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
 golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
 golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
@@ -73,6 +75,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
 golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
 golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
+golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
+golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -86,6 +90,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
 golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
+golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
diff --git a/source/html/sync.go b/source/html/sync.go
index e7f1ee7..b2f619b 100644
--- a/source/html/sync.go
+++ b/source/html/sync.go
@@ -169,24 +169,14 @@ func getAllDestinationFilesWithoutExcludes(destination types.Destination) ([]str
 
 func SyncHtml(p string) error {
 
-	content, err := os.ReadFile(p)
-	if err != nil {
-		return err
-	}
-
 	currentDir, _ := os.Getwd()
-	if err := os.Chdir(filepath.Dir(p)); err != nil {
-		return err
-	}
-
 	defer func() {
 		_ = os.Chdir(currentDir)
 	}()
 
-	specification := types.SyncSpecification{}
-
-	if err := yaml.Unmarshal(content, &specification); err != nil {
-		return err
+	err, specification, err2 := readSpec(p)
+	if err2 != nil {
+		return err2
 	}
 
 	var sourceFiles StringNodeMap
@@ -237,11 +227,6 @@ func SyncHtml(p string) error {
 				destinationSelector = sourceSelector
 			}
 
-			query, err := cascadia.Compile(destinationSelector)
-			if err != nil {
-				return err
-			}
-
 			keepMap := make(StringListNodeMap)
 
 			for _, n := range r.Destination.Keep {
@@ -252,24 +237,46 @@ func SyncHtml(p string) error {
 
 				kNode := q.MatchAll(destinationFiles[d])
 				if kNode == nil {
-					fmt.Errorf("keep node not found: %s", n)
+					fmt.Println("keep node %s not found in %s", n, d)
 					continue
 				}
 
 				for _, k := range kNode {
-					keepMap[n] = append(keepMap[n], k)
+					keepMap[n] = append(keepMap[n], engine.CloneNode(k))
 				}
 
 			}
 
+			query, err := cascadia.Compile(destinationSelector)
+			if err != nil {
+				return err
+			}
+
 			destinationData := query.MatchFirst(destinationFiles[d])
 			if destinationData == nil {
 				return fmt.Errorf("could not find destination selector %s in %s", destinationSelector, d)
 			}
 
 			n := engine.CloneNode(sourceNode)
-			destinationData.Parent.InsertBefore(n, destinationData)
-			destinationData.Parent.RemoveChild(destinationData)
+			destinationParent := destinationData.Parent
+			if destinationParent == nil {
+				return fmt.Errorf("destination parent is nil")
+			}
+
+			content := strings.Builder{}
+			_ = html.Render(&content, n)
+			cc := content.String()
+			_ = cc
+
+			destinationParent.InsertBefore(n, destinationData)
+
+			_ = html.Render(&content, n)
+			cc = content.String()
+
+			destinationParent.RemoveChild(destinationData)
+
+			_ = html.Render(&content, n)
+			cc = content.String()
 
 			for sel, k := range keepMap {
 				cas, err := cascadia.Compile(sel)
@@ -278,7 +285,11 @@ func SyncHtml(p string) error {
 				}
 				x := cas.MatchAll(destinationFiles[d])
 				if x == nil {
-					return fmt.Errorf("could not find selector %s in %s", sel, d)
+					for _, kk := range k {
+						ckk := engine.CloneNode(kk)
+						query.MatchFirst(destinationParent).AppendChild(ckk)
+					}
+					continue
 				}
 
 				for _, n1 := range x {
@@ -310,7 +321,11 @@ func SyncHtml(p string) error {
 		if err != nil {
 			return err
 		}
-		err = html.Render(fp, destinationFiles[d])
+
+		htmlContent := destinationFiles[d]
+		cleanHTML(htmlContent)
+
+		err = html.Render(fp, htmlContent)
 		err2 := fp.Close()
 		if err2 != nil {
 			return err2
@@ -325,6 +340,44 @@ func SyncHtml(p string) error {
 	return nil
 }
 
+// cleanHTML removes trailing newlines before </body>
+func cleanHTML(n *html.Node) {
+	if n.Type == html.ElementNode && n.Data == "body" {
+		// Remove text nodes containing only whitespace at the end of the body
+		for n.LastChild != nil && isWhitespaceNode(n.LastChild) {
+			n.RemoveChild(n.LastChild)
+		}
+	}
+
+	// Recursively clean child nodes
+	for c := n.FirstChild; c != nil; c = c.NextSibling {
+		cleanHTML(c)
+	}
+}
+
+// isWhitespaceNode checks if a node is a text node containing only whitespace
+func isWhitespaceNode(n *html.Node) bool {
+	return n.Type == html.TextNode && strings.TrimSpace(n.Data) == ""
+}
+
+func readSpec(p string) (error, types.SyncSpecification, error) {
+	content, err := os.ReadFile(p)
+	if err != nil {
+		return nil, types.SyncSpecification{}, err
+	}
+
+	if err := os.Chdir(filepath.Dir(p)); err != nil {
+		return nil, types.SyncSpecification{}, err
+	}
+
+	specification := types.SyncSpecification{}
+
+	if err := yaml.Unmarshal(content, &specification); err != nil {
+		return nil, types.SyncSpecification{}, err
+	}
+	return err, specification, nil
+}
+
 func checkExcludes(exclude []string, d string) (bool, error) {
 	for _, e := range exclude {
 		e, err := filepath.Abs(e)
diff --git a/source/vendor/golang.org/x/crypto/bcrypt/bcrypt.go b/source/vendor/golang.org/x/crypto/bcrypt/bcrypt.go
index 5577c0f..dc93118 100644
--- a/source/vendor/golang.org/x/crypto/bcrypt/bcrypt.go
+++ b/source/vendor/golang.org/x/crypto/bcrypt/bcrypt.go
@@ -4,7 +4,7 @@
 
 // Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing
 // algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf
-package bcrypt // import "golang.org/x/crypto/bcrypt"
+package bcrypt
 
 // The code is a port of Provos and Mazières's C implementation.
 import (
diff --git a/source/vendor/golang.org/x/crypto/blowfish/cipher.go b/source/vendor/golang.org/x/crypto/blowfish/cipher.go
index 213bf20..0898956 100644
--- a/source/vendor/golang.org/x/crypto/blowfish/cipher.go
+++ b/source/vendor/golang.org/x/crypto/blowfish/cipher.go
@@ -11,7 +11,7 @@
 // Deprecated: any new system should use AES (from crypto/aes, if necessary in
 // an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from
 // golang.org/x/crypto/chacha20poly1305).
-package blowfish // import "golang.org/x/crypto/blowfish"
+package blowfish
 
 // The code is a port of Bruce Schneier's C implementation.
 // See https://www.schneier.com/blowfish.html.
diff --git a/source/vendor/golang.org/x/sys/unix/mremap.go b/source/vendor/golang.org/x/sys/unix/mremap.go
index fd45fe5..3a5e776 100644
--- a/source/vendor/golang.org/x/sys/unix/mremap.go
+++ b/source/vendor/golang.org/x/sys/unix/mremap.go
@@ -50,3 +50,8 @@ func (m *mremapMmapper) Mremap(oldData []byte, newLength int, flags int) (data [
 func Mremap(oldData []byte, newLength int, flags int) (data []byte, err error) {
 	return mapper.Mremap(oldData, newLength, flags)
 }
+
+func MremapPtr(oldAddr unsafe.Pointer, oldSize uintptr, newAddr unsafe.Pointer, newSize uintptr, flags int) (ret unsafe.Pointer, err error) {
+	xaddr, err := mapper.mremap(uintptr(oldAddr), oldSize, newSize, flags, uintptr(newAddr))
+	return unsafe.Pointer(xaddr), err
+}
diff --git a/source/vendor/golang.org/x/sys/unix/syscall_darwin.go b/source/vendor/golang.org/x/sys/unix/syscall_darwin.go
index 59542a8..4cc7b00 100644
--- a/source/vendor/golang.org/x/sys/unix/syscall_darwin.go
+++ b/source/vendor/golang.org/x/sys/unix/syscall_darwin.go
@@ -542,6 +542,18 @@ func SysctlKinfoProcSlice(name string, args ...int) ([]KinfoProc, error) {
 	}
 }
 
+//sys	pthread_chdir_np(path string) (err error)
+
+func PthreadChdir(path string) (err error) {
+	return pthread_chdir_np(path)
+}
+
+//sys	pthread_fchdir_np(fd int) (err error)
+
+func PthreadFchdir(fd int) (err error) {
+	return pthread_fchdir_np(fd)
+}
+
 //sys	sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error)
 
 //sys	shmat(id int, addr uintptr, flag int) (ret uintptr, err error)
diff --git a/source/vendor/golang.org/x/sys/unix/syscall_unix.go b/source/vendor/golang.org/x/sys/unix/syscall_unix.go
index 77081de..4e92e5a 100644
--- a/source/vendor/golang.org/x/sys/unix/syscall_unix.go
+++ b/source/vendor/golang.org/x/sys/unix/syscall_unix.go
@@ -154,6 +154,15 @@ func Munmap(b []byte) (err error) {
 	return mapper.Munmap(b)
 }
 
+func MmapPtr(fd int, offset int64, addr unsafe.Pointer, length uintptr, prot int, flags int) (ret unsafe.Pointer, err error) {
+	xaddr, err := mapper.mmap(uintptr(addr), length, prot, flags, fd, offset)
+	return unsafe.Pointer(xaddr), err
+}
+
+func MunmapPtr(addr unsafe.Pointer, length uintptr) (err error) {
+	return mapper.munmap(uintptr(addr), length)
+}
+
 func Read(fd int, p []byte) (n int, err error) {
 	n, err = read(fd, p)
 	if raceenabled {
diff --git a/source/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go b/source/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go
index ccb02f2..07642c3 100644
--- a/source/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go
+++ b/source/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go
@@ -760,6 +760,39 @@ var libc_sysctl_trampoline_addr uintptr
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func pthread_chdir_np(path string) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := syscall_syscall(libc_pthread_chdir_np_trampoline_addr, uintptr(unsafe.Pointer(_p0)), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+var libc_pthread_chdir_np_trampoline_addr uintptr
+
+//go:cgo_import_dynamic libc_pthread_chdir_np pthread_chdir_np "/usr/lib/libSystem.B.dylib"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func pthread_fchdir_np(fd int) (err error) {
+	_, _, e1 := syscall_syscall(libc_pthread_fchdir_np_trampoline_addr, uintptr(fd), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+var libc_pthread_fchdir_np_trampoline_addr uintptr
+
+//go:cgo_import_dynamic libc_pthread_fchdir_np pthread_fchdir_np "/usr/lib/libSystem.B.dylib"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error) {
 	_, _, e1 := syscall_syscall6(libc_sendfile_trampoline_addr, uintptr(infd), uintptr(outfd), uintptr(offset), uintptr(unsafe.Pointer(len)), uintptr(hdtr), uintptr(flags))
 	if e1 != 0 {
diff --git a/source/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s b/source/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s
index 8b8bb28..923e08c 100644
--- a/source/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s
+++ b/source/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s
@@ -228,6 +228,16 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0
 GLOBL	·libc_sysctl_trampoline_addr(SB), RODATA, $8
 DATA	·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB)
 
+TEXT libc_pthread_chdir_np_trampoline<>(SB),NOSPLIT,$0-0
+	JMP	libc_pthread_chdir_np(SB)
+GLOBL	·libc_pthread_chdir_np_trampoline_addr(SB), RODATA, $8
+DATA	·libc_pthread_chdir_np_trampoline_addr(SB)/8, $libc_pthread_chdir_np_trampoline<>(SB)
+
+TEXT libc_pthread_fchdir_np_trampoline<>(SB),NOSPLIT,$0-0
+	JMP	libc_pthread_fchdir_np(SB)
+GLOBL	·libc_pthread_fchdir_np_trampoline_addr(SB), RODATA, $8
+DATA	·libc_pthread_fchdir_np_trampoline_addr(SB)/8, $libc_pthread_fchdir_np_trampoline<>(SB)
+
 TEXT libc_sendfile_trampoline<>(SB),NOSPLIT,$0-0
 	JMP	libc_sendfile(SB)
 GLOBL	·libc_sendfile_trampoline_addr(SB), RODATA, $8
diff --git a/source/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go b/source/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go
index 1b40b99..7d73dda 100644
--- a/source/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go
+++ b/source/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go
@@ -760,6 +760,39 @@ var libc_sysctl_trampoline_addr uintptr
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func pthread_chdir_np(path string) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := syscall_syscall(libc_pthread_chdir_np_trampoline_addr, uintptr(unsafe.Pointer(_p0)), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+var libc_pthread_chdir_np_trampoline_addr uintptr
+
+//go:cgo_import_dynamic libc_pthread_chdir_np pthread_chdir_np "/usr/lib/libSystem.B.dylib"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func pthread_fchdir_np(fd int) (err error) {
+	_, _, e1 := syscall_syscall(libc_pthread_fchdir_np_trampoline_addr, uintptr(fd), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+var libc_pthread_fchdir_np_trampoline_addr uintptr
+
+//go:cgo_import_dynamic libc_pthread_fchdir_np pthread_fchdir_np "/usr/lib/libSystem.B.dylib"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error) {
 	_, _, e1 := syscall_syscall6(libc_sendfile_trampoline_addr, uintptr(infd), uintptr(outfd), uintptr(offset), uintptr(unsafe.Pointer(len)), uintptr(hdtr), uintptr(flags))
 	if e1 != 0 {
diff --git a/source/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s b/source/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s
index 08362c1..0577001 100644
--- a/source/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s
+++ b/source/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s
@@ -228,6 +228,16 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0
 GLOBL	·libc_sysctl_trampoline_addr(SB), RODATA, $8
 DATA	·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB)
 
+TEXT libc_pthread_chdir_np_trampoline<>(SB),NOSPLIT,$0-0
+	JMP	libc_pthread_chdir_np(SB)
+GLOBL	·libc_pthread_chdir_np_trampoline_addr(SB), RODATA, $8
+DATA	·libc_pthread_chdir_np_trampoline_addr(SB)/8, $libc_pthread_chdir_np_trampoline<>(SB)
+
+TEXT libc_pthread_fchdir_np_trampoline<>(SB),NOSPLIT,$0-0
+	JMP	libc_pthread_fchdir_np(SB)
+GLOBL	·libc_pthread_fchdir_np_trampoline_addr(SB), RODATA, $8
+DATA	·libc_pthread_fchdir_np_trampoline_addr(SB)/8, $libc_pthread_fchdir_np_trampoline<>(SB)
+
 TEXT libc_sendfile_trampoline<>(SB),NOSPLIT,$0-0
 	JMP	libc_sendfile(SB)
 GLOBL	·libc_sendfile_trampoline_addr(SB), RODATA, $8
diff --git a/source/vendor/golang.org/x/sys/windows/security_windows.go b/source/vendor/golang.org/x/sys/windows/security_windows.go
index 6f7d2ac..97651b5 100644
--- a/source/vendor/golang.org/x/sys/windows/security_windows.go
+++ b/source/vendor/golang.org/x/sys/windows/security_windows.go
@@ -894,7 +894,7 @@ type ACL struct {
 	aclRevision byte
 	sbz1        byte
 	aclSize     uint16
-	aceCount    uint16
+	AceCount    uint16
 	sbz2        uint16
 }
 
@@ -1087,6 +1087,27 @@ type EXPLICIT_ACCESS struct {
 	Trustee           TRUSTEE
 }
 
+// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-ace_header
+type ACE_HEADER struct {
+	AceType  uint8
+	AceFlags uint8
+	AceSize  uint16
+}
+
+// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-access_allowed_ace
+type ACCESS_ALLOWED_ACE struct {
+	Header   ACE_HEADER
+	Mask     ACCESS_MASK
+	SidStart uint32
+}
+
+const (
+	// Constants for AceType
+	// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-ace_header
+	ACCESS_ALLOWED_ACE_TYPE = 0
+	ACCESS_DENIED_ACE_TYPE  = 1
+)
+
 // This type is the union inside of TRUSTEE and must be created using one of the TrusteeValueFrom* functions.
 type TrusteeValue uintptr
 
@@ -1158,6 +1179,7 @@ type OBJECTS_AND_NAME struct {
 //sys	makeSelfRelativeSD(absoluteSD *SECURITY_DESCRIPTOR, selfRelativeSD *SECURITY_DESCRIPTOR, selfRelativeSDSize *uint32) (err error) = advapi32.MakeSelfRelativeSD
 
 //sys	setEntriesInAcl(countExplicitEntries uint32, explicitEntries *EXPLICIT_ACCESS, oldACL *ACL, newACL **ACL) (ret error) = advapi32.SetEntriesInAclW
+//sys	GetAce(acl *ACL, aceIndex uint32, pAce **ACCESS_ALLOWED_ACE) (ret error) = advapi32.GetAce
 
 // Control returns the security descriptor control bits.
 func (sd *SECURITY_DESCRIPTOR) Control() (control SECURITY_DESCRIPTOR_CONTROL, revision uint32, err error) {
diff --git a/source/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/source/vendor/golang.org/x/sys/windows/zsyscall_windows.go
index 9f73df7..eba7610 100644
--- a/source/vendor/golang.org/x/sys/windows/zsyscall_windows.go
+++ b/source/vendor/golang.org/x/sys/windows/zsyscall_windows.go
@@ -91,6 +91,7 @@ var (
 	procEnumServicesStatusExW                                = modadvapi32.NewProc("EnumServicesStatusExW")
 	procEqualSid                                             = modadvapi32.NewProc("EqualSid")
 	procFreeSid                                              = modadvapi32.NewProc("FreeSid")
+	procGetAce                                               = modadvapi32.NewProc("GetAce")
 	procGetLengthSid                                         = modadvapi32.NewProc("GetLengthSid")
 	procGetNamedSecurityInfoW                                = modadvapi32.NewProc("GetNamedSecurityInfoW")
 	procGetSecurityDescriptorControl                         = modadvapi32.NewProc("GetSecurityDescriptorControl")
@@ -1224,6 +1225,14 @@ func setEntriesInAcl(countExplicitEntries uint32, explicitEntries *EXPLICIT_ACCE
 	return
 }
 
+func GetAce(acl *ACL, aceIndex uint32, pAce **ACCESS_ALLOWED_ACE) (ret error) {
+	r0, _, _ := syscall.Syscall(procGetAce.Addr(), 3, uintptr(unsafe.Pointer(acl)), uintptr(aceIndex), uintptr(unsafe.Pointer(pAce)))
+	if r0 == 0 {
+		ret = GetLastError()
+	}
+	return
+}
+
 func SetKernelObjectSecurity(handle Handle, securityInformation SECURITY_INFORMATION, securityDescriptor *SECURITY_DESCRIPTOR) (err error) {
 	r1, _, e1 := syscall.Syscall(procSetKernelObjectSecurity.Addr(), 3, uintptr(handle), uintptr(securityInformation), uintptr(unsafe.Pointer(securityDescriptor)))
 	if r1 == 0 {
diff --git a/source/vendor/modules.txt b/source/vendor/modules.txt
index 5558626..9ecbc2c 100644
--- a/source/vendor/modules.txt
+++ b/source/vendor/modules.txt
@@ -54,8 +54,8 @@ gitlab.schukai.com/oss/libraries/go/utilities/data.git
 # gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.9.2
 ## explicit; go 1.20
 gitlab.schukai.com/oss/libraries/go/utilities/pathfinder
-# golang.org/x/crypto v0.24.0
-## explicit; go 1.18
+# golang.org/x/crypto v0.25.0
+## explicit; go 1.20
 golang.org/x/crypto/bcrypt
 golang.org/x/crypto/blowfish
 # golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8
@@ -65,11 +65,11 @@ golang.org/x/exp/slices
 golang.org/x/exp/slog
 golang.org/x/exp/slog/internal
 golang.org/x/exp/slog/internal/buffer
-# golang.org/x/net v0.26.0
+# golang.org/x/net v0.27.0
 ## explicit; go 1.18
 golang.org/x/net/html
 golang.org/x/net/html/atom
-# golang.org/x/sys v0.21.0
+# golang.org/x/sys v0.22.0
 ## explicit; go 1.18
 golang.org/x/sys/unix
 golang.org/x/sys/windows
-- 
GitLab