52 lines
1.3 KiB
Go
52 lines
1.3 KiB
Go
package target
|
|
|
|
import (
|
|
"errors"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"golang.org/x/sys/unix"
|
|
|
|
"proxyctl/internal/profile"
|
|
)
|
|
|
|
// ReadOnlyWriteAuthority observes whether the current process can write the
|
|
// configured locations. It never creates a probe file or changes permissions.
|
|
func ReadOnlyWriteAuthority(paths ...string) Check {
|
|
check := Check{Name: "write authority", OK: true}
|
|
var evidence []string
|
|
for _, path := range paths {
|
|
ok, detail := readOnlyWritablePath(path)
|
|
check.OK = check.OK && ok
|
|
evidence = append(evidence, detail)
|
|
}
|
|
check.Evidence = profile.RedactText(strings.Join(evidence, "; "))
|
|
return check
|
|
}
|
|
|
|
func readOnlyWritablePath(path string) (bool, string) {
|
|
candidate := filepath.Clean(path)
|
|
for {
|
|
info, err := os.Stat(candidate)
|
|
if err == nil {
|
|
if !info.IsDir() {
|
|
candidate = filepath.Dir(candidate)
|
|
}
|
|
if err := unix.Access(candidate, unix.W_OK); err != nil {
|
|
return false, candidate + " is not writable: " + err.Error()
|
|
}
|
|
return true, candidate + " is writable"
|
|
}
|
|
if !errors.Is(err, fs.ErrNotExist) {
|
|
return false, candidate + " cannot be inspected: " + err.Error()
|
|
}
|
|
parent := filepath.Dir(candidate)
|
|
if parent == candidate {
|
|
return false, path + " has no writable existing ancestor"
|
|
}
|
|
candidate = parent
|
|
}
|
|
}
|