refactor(ax): align remaining AX examples and names
Some checks failed
CI / test (push) Failing after 3s
CI / auto-fix (push) Failing after 0s
CI / auto-merge (push) Failing after 0s

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-03-31 14:27:58 +00:00
parent 15b6074e46
commit c713bafd48
14 changed files with 137 additions and 141 deletions

View file

@ -227,8 +227,8 @@ func TestDataNode_List_Good(t *testing.T) {
require.NoError(t, err)
names := make([]string, len(entries))
for i, e := range entries {
names[i] = e.Name()
for index, entry := range entries {
names[index] = entry.Name()
}
assert.Contains(t, names, "root.txt")
assert.Contains(t, names, "pkg")
@ -236,8 +236,8 @@ func TestDataNode_List_Good(t *testing.T) {
entries, err = dataNodeMedium.List("pkg")
require.NoError(t, err)
names = make([]string, len(entries))
for i, e := range entries {
names[i] = e.Name()
for index, entry := range entries {
names[index] = entry.Name()
}
assert.Contains(t, names, "a.go")
assert.Contains(t, names, "b.go")
@ -264,11 +264,11 @@ func TestDataNode_Open_Good(t *testing.T) {
require.NoError(t, dataNodeMedium.Write("open.txt", "opened"))
f, err := dataNodeMedium.Open("open.txt")
file, err := dataNodeMedium.Open("open.txt")
require.NoError(t, err)
defer f.Close()
defer file.Close()
data, err := io.ReadAll(f)
data, err := io.ReadAll(file)
require.NoError(t, err)
assert.Equal(t, "opened", string(data))
}
@ -276,19 +276,19 @@ func TestDataNode_Open_Good(t *testing.T) {
func TestDataNode_CreateAppend_Good(t *testing.T) {
dataNodeMedium := New()
w, err := dataNodeMedium.Create("new.txt")
writer, err := dataNodeMedium.Create("new.txt")
require.NoError(t, err)
w.Write([]byte("hello"))
w.Close()
_, _ = writer.Write([]byte("hello"))
require.NoError(t, writer.Close())
got, err := dataNodeMedium.Read("new.txt")
require.NoError(t, err)
assert.Equal(t, "hello", got)
w, err = dataNodeMedium.Append("new.txt")
writer, err = dataNodeMedium.Append("new.txt")
require.NoError(t, err)
w.Write([]byte(" world"))
w.Close()
_, _ = writer.Write([]byte(" world"))
require.NoError(t, writer.Close())
got, err = dataNodeMedium.Read("new.txt")
require.NoError(t, err)
@ -315,17 +315,17 @@ func TestDataNode_Append_ReadFailure_Bad(t *testing.T) {
func TestDataNode_Streams_Good(t *testing.T) {
dataNodeMedium := New()
ws, err := dataNodeMedium.WriteStream("stream.txt")
writeStream, err := dataNodeMedium.WriteStream("stream.txt")
require.NoError(t, err)
ws.Write([]byte("streamed"))
ws.Close()
_, _ = writeStream.Write([]byte("streamed"))
require.NoError(t, writeStream.Close())
rs, err := dataNodeMedium.ReadStream("stream.txt")
readStream, err := dataNodeMedium.ReadStream("stream.txt")
require.NoError(t, err)
data, err := io.ReadAll(rs)
data, err := io.ReadAll(readStream)
require.NoError(t, err)
assert.Equal(t, "streamed", string(data))
rs.Close()
require.NoError(t, readStream.Close())
}
func TestDataNode_SnapshotRestore_Good(t *testing.T) {
@ -334,11 +334,11 @@ func TestDataNode_SnapshotRestore_Good(t *testing.T) {
require.NoError(t, dataNodeMedium.Write("a.txt", "alpha"))
require.NoError(t, dataNodeMedium.Write("b/c.txt", "charlie"))
snap, err := dataNodeMedium.Snapshot()
snapshotData, err := dataNodeMedium.Snapshot()
require.NoError(t, err)
assert.NotEmpty(t, snap)
assert.NotEmpty(t, snapshotData)
restoredNode, err := FromTar(snap)
restoredNode, err := FromTar(snapshotData)
require.NoError(t, err)
got, err := restoredNode.Read("a.txt")
@ -355,13 +355,13 @@ func TestDataNode_Restore_Good(t *testing.T) {
require.NoError(t, dataNodeMedium.Write("original.txt", "before"))
snap, err := dataNodeMedium.Snapshot()
snapshotData, err := dataNodeMedium.Snapshot()
require.NoError(t, err)
require.NoError(t, dataNodeMedium.Write("original.txt", "after"))
require.NoError(t, dataNodeMedium.Write("extra.txt", "extra"))
require.NoError(t, dataNodeMedium.Restore(snap))
require.NoError(t, dataNodeMedium.Restore(snapshotData))
got, err := dataNodeMedium.Read("original.txt")
require.NoError(t, err)
@ -375,14 +375,14 @@ func TestDataNode_DataNode_Good(t *testing.T) {
require.NoError(t, dataNodeMedium.Write("test.txt", "borg"))
dn := dataNodeMedium.DataNode()
assert.NotNil(t, dn)
dataNode := dataNodeMedium.DataNode()
assert.NotNil(t, dataNode)
f, err := dn.Open("test.txt")
file, err := dataNode.Open("test.txt")
require.NoError(t, err)
defer f.Close()
defer file.Close()
data, err := io.ReadAll(f)
data, err := io.ReadAll(file)
require.NoError(t, err)
assert.Equal(t, "borg", string(data))
}

View file

@ -139,7 +139,7 @@ keyValueStore, _ := store.New(store.Options{Path: ":memory:"})
keyValueStore.Set("user", "pool", "pool.lthn.io:3333")
keyValueStore.Set("user", "wallet", "iz...")
renderedText, _ := keyValueStore.Render(`{"pool":"{{ .pool }}"}`, "user")
// renderedText: {"pool":"pool.lthn.io:3333"}
assert.Equal(t, `{"pool":"pool.lthn.io:3333"}`, renderedText)
```
### store.Medium (Medium adapter)
@ -164,8 +164,8 @@ The sigil package implements composable, reversible data transformations.
```go
type Sigil interface {
In(data []byte) ([]byte, error) // forward transform
Out(data []byte) ([]byte, error) // reverse transform
In(data []byte) ([]byte, error)
Out(data []byte) ([]byte, error)
}
```
@ -199,10 +199,8 @@ Created via `NewSigil(name)`:
### Pipeline Functions
```go
// Apply sigils left-to-right.
encoded, _ := sigil.Transmute(data, []sigil.Sigil{gzipSigil, hexSigil})
// Reverse sigils right-to-left.
original, _ := sigil.Untransmute(encoded, []sigil.Sigil{gzipSigil, hexSigil})
```
@ -231,12 +229,11 @@ The pre-obfuscation layer ensures that raw plaintext patterns are never sent dir
key := make([]byte, 32)
rand.Read(key)
s, _ := sigil.NewChaChaPolySigil(key, nil)
ciphertext, _ := s.In([]byte("secret"))
plaintext, _ := s.Out(ciphertext)
cipherSigil, _ := sigil.NewChaChaPolySigil(key, nil)
ciphertext, _ := cipherSigil.In([]byte("secret"))
plaintext, _ := cipherSigil.Out(ciphertext)
// With stronger obfuscation:
s2, _ := sigil.NewChaChaPolySigil(key, &sigil.ShuffleMaskObfuscator{})
shuffleCipherSigil, _ := sigil.NewChaChaPolySigil(key, &sigil.ShuffleMaskObfuscator{})
```
Each call to `In` generates a fresh random nonce, so encrypting the same plaintext twice produces different ciphertexts.

View file

@ -96,7 +96,6 @@ func TestMyFeature(t *testing.T) {
_ = memoryMedium.Write("config.yaml", "key: value")
_ = memoryMedium.EnsureDir("data")
// Your code under test receives memoryMedium as an io.Medium
result, err := myFunction(memoryMedium)
assert.NoError(t, err)
output, err := memoryMedium.Read("output.txt")

View file

@ -19,21 +19,17 @@ import (
"forge.lthn.ai/core/go-io/node"
)
// Use the pre-initialised local filesystem (unsandboxed, rooted at "/").
content, _ := io.Local.Read("/etc/hostname")
// Create a sandboxed medium restricted to a single directory.
sandbox, _ := io.NewSandboxed("/var/data/myapp")
_ = sandbox.Write("config.yaml", "key: value")
sandboxMedium, _ := io.NewSandboxed("/var/data/myapp")
_ = sandboxMedium.Write("config.yaml", "key: value")
// In-memory filesystem with tar serialisation.
mem := node.New()
mem.AddData("hello.txt", []byte("world"))
tarball, _ := mem.ToTar()
nodeTree := node.New()
nodeTree.AddData("hello.txt", []byte("world"))
tarball, _ := nodeTree.ToTar()
// S3 backend (requires an *awss3.Client from the AWS SDK).
bucket, _ := s3.New(s3.Options{Bucket: "my-bucket", Client: awsClient, Prefix: "uploads/"})
_ = bucket.Write("photo.jpg", rawData)
s3Medium, _ := s3.New(s3.Options{Bucket: "my-bucket", Client: awsClient, Prefix: "uploads/"})
_ = s3Medium.Write("photo.jpg", rawData)
```
@ -58,29 +54,24 @@ Every storage backend implements the same 17-method interface:
```go
type Medium interface {
// Content operations
Read(path string) (string, error)
Write(path, content string) error
WriteMode(path, content string, mode fs.FileMode) error
// Streaming (for large files)
ReadStream(path string) (io.ReadCloser, error)
WriteStream(path string) (io.WriteCloser, error)
Open(path string) (fs.File, error)
Create(path string) (io.WriteCloser, error)
Append(path string) (io.WriteCloser, error)
// Directory operations
EnsureDir(path string) error
List(path string) ([]fs.DirEntry, error)
// Metadata
Stat(path string) (fs.FileInfo, error)
Exists(path string) bool
IsFile(path string) bool
IsDir(path string) bool
// Mutation
Delete(path string) error
DeleteAll(path string) error
Rename(oldPath, newPath string) error
@ -95,12 +86,12 @@ All backends implement this interface fully. Backends where a method has no natu
The root package provides helper functions that accept any `Medium`:
```go
// Copy a file between any two backends.
err := io.Copy(localMedium, "source.txt", s3Medium, "dest.txt")
sourceMedium := io.Local
destinationMedium := io.NewMemoryMedium()
err := io.Copy(sourceMedium, "source.txt", destinationMedium, "dest.txt")
// Read/Write wrappers that take an explicit medium.
content, err := io.Read(medium, "path")
err := io.Write(medium, "path", "content")
content, err := io.Read(destinationMedium, "path")
err = io.Write(destinationMedium, "path", "content")
```

View file

@ -30,23 +30,29 @@ func New(root string) (*Medium, error) {
}
func dirSeparator() string {
if sep := core.Env("DS"); sep != "" {
return sep
if separator := core.Env("CORE_PATH_SEPARATOR"); separator != "" {
return separator
}
if separator := core.Env("DS"); separator != "" {
return separator
}
return "/"
}
func normalisePath(path string) string {
sep := dirSeparator()
if sep == "/" {
return core.Replace(path, "\\", sep)
separator := dirSeparator()
if separator == "/" {
return core.Replace(path, "\\", separator)
}
return core.Replace(path, "/", sep)
return core.Replace(path, "/", separator)
}
func currentWorkingDir() string {
if cwd := core.Env("DIR_CWD"); cwd != "" {
return cwd
if workingDirectory := core.Env("CORE_WORKING_DIRECTORY"); workingDirectory != "" {
return workingDirectory
}
if workingDirectory := core.Env("DIR_CWD"); workingDirectory != "" {
return workingDirectory
}
return "."
}

View file

@ -40,8 +40,8 @@ func TestLocal_Path_RootFilesystem_Good(t *testing.T) {
assert.Equal(t, "/etc/passwd", localMedium.sandboxedPath("/etc/passwd"))
assert.Equal(t, "/home/user/file.txt", localMedium.sandboxedPath("/home/user/file.txt"))
cwd := currentWorkingDir()
assert.Equal(t, core.Path(cwd, "file.txt"), localMedium.sandboxedPath("file.txt"))
workingDirectory := currentWorkingDir()
assert.Equal(t, core.Path(workingDirectory, "file.txt"), localMedium.sandboxedPath("file.txt"))
}
func TestLocal_ReadWrite_Basic_Good(t *testing.T) {
@ -303,8 +303,8 @@ func TestLocal_List_Good(t *testing.T) {
assert.Len(t, entries, 3)
names := make(map[string]bool)
for _, e := range entries {
names[e.Name()] = true
for _, entry := range entries {
names[entry.Name()] = true
}
assert.True(t, names["file1.txt"])
assert.True(t, names["file2.txt"])

View file

@ -167,8 +167,8 @@ func TestMemoryMedium_List_Good(t *testing.T) {
assert.Len(t, entries, 3)
names := make(map[string]bool)
for _, e := range entries {
names[e.Name()] = true
for _, entry := range entries {
names[entry.Name()] = true
}
assert.True(t, names["file1.txt"])
assert.True(t, names["file2.txt"])

View file

@ -556,8 +556,8 @@ func TestNode_FSInterface_Good(t *testing.T) {
func sortedNames(entries []fs.DirEntry) []string {
var names []string
for _, e := range entries {
names = append(names, e.Name())
for _, entry := range entries {
names = append(names, entry.Name())
}
sort.Strings(names)
return names

View file

@ -409,8 +409,8 @@ func TestS3_List_Good(t *testing.T) {
require.NoError(t, err)
names := make(map[string]bool)
for _, e := range entries {
names[e.Name()] = true
for _, entry := range entries {
names[entry.Name()] = true
}
assert.True(t, names["file1.txt"], "should list file1.txt")
@ -418,10 +418,10 @@ func TestS3_List_Good(t *testing.T) {
assert.True(t, names["sub"], "should list sub directory")
assert.Len(t, entries, 3)
for _, e := range entries {
if e.Name() == "sub" {
assert.True(t, e.IsDir())
info, err := e.Info()
for _, entry := range entries {
if entry.Name() == "sub" {
assert.True(t, entry.IsDir())
info, err := entry.Info()
require.NoError(t, err)
assert.True(t, info.IsDir())
}
@ -438,8 +438,8 @@ func TestS3_List_Root_Good(t *testing.T) {
require.NoError(t, err)
names := make(map[string]bool)
for _, e := range entries {
names[e.Name()] = true
for _, entry := range entries {
names[entry.Name()] = true
}
assert.True(t, names["root.txt"])
@ -476,15 +476,15 @@ func TestS3_Open_Good(t *testing.T) {
require.NoError(t, s3Medium.Write("file.txt", "open me"))
f, err := s3Medium.Open("file.txt")
file, err := s3Medium.Open("file.txt")
require.NoError(t, err)
defer f.Close()
defer file.Close()
data, err := goio.ReadAll(f.(goio.Reader))
data, err := goio.ReadAll(file.(goio.Reader))
require.NoError(t, err)
assert.Equal(t, "open me", string(data))
stat, err := f.Stat()
stat, err := file.Stat()
require.NoError(t, err)
assert.Equal(t, "file.txt", stat.Name())
}
@ -499,14 +499,14 @@ func TestS3_Open_NotFound_Bad(t *testing.T) {
func TestS3_Create_Good(t *testing.T) {
s3Medium, _ := newS3Medium(t)
w, err := s3Medium.Create("new.txt")
writer, err := s3Medium.Create("new.txt")
require.NoError(t, err)
n, err := w.Write([]byte("created"))
bytesWritten, err := writer.Write([]byte("created"))
require.NoError(t, err)
assert.Equal(t, 7, n)
assert.Equal(t, 7, bytesWritten)
err = w.Close()
err = writer.Close()
require.NoError(t, err)
content, err := s3Medium.Read("new.txt")
@ -519,12 +519,12 @@ func TestS3_Append_Good(t *testing.T) {
require.NoError(t, s3Medium.Write("append.txt", "hello"))
w, err := s3Medium.Append("append.txt")
writer, err := s3Medium.Append("append.txt")
require.NoError(t, err)
_, err = w.Write([]byte(" world"))
_, err = writer.Write([]byte(" world"))
require.NoError(t, err)
err = w.Close()
err = writer.Close()
require.NoError(t, err)
content, err := s3Medium.Read("append.txt")
@ -535,12 +535,12 @@ func TestS3_Append_Good(t *testing.T) {
func TestS3_Append_NewFile_Good(t *testing.T) {
s3Medium, _ := newS3Medium(t)
w, err := s3Medium.Append("new.txt")
writer, err := s3Medium.Append("new.txt")
require.NoError(t, err)
_, err = w.Write([]byte("fresh"))
_, err = writer.Write([]byte("fresh"))
require.NoError(t, err)
err = w.Close()
err = writer.Close()
require.NoError(t, err)
content, err := s3Medium.Read("new.txt")

View file

@ -331,7 +331,7 @@ func TestCryptoSigil_ChaChaPolySigil_TamperedCiphertext_Bad(t *testing.T) {
type failReader struct{}
func (f *failReader) Read([]byte) (int, error) {
func (reader *failReader) Read([]byte) (int, error) {
return 0, core.NewError("entropy source failed")
}
@ -432,8 +432,8 @@ func isHex(data []byte) bool {
type failSigil struct{}
func (f *failSigil) In([]byte) ([]byte, error) { return nil, core.NewError("fail in") }
func (f *failSigil) Out([]byte) ([]byte, error) { return nil, core.NewError("fail out") }
func (sigil *failSigil) In([]byte) ([]byte, error) { return nil, core.NewError("fail in") }
func (sigil *failSigil) Out([]byte) ([]byte, error) { return nil, core.NewError("fail out") }
func TestCryptoSigil_Transmute_ErrorPropagation_Bad(t *testing.T) {
_, err := Transmute([]byte("data"), []Sigil{&failSigil{}})

View file

@ -282,8 +282,8 @@ func TestSqlite_List_Good(t *testing.T) {
require.NoError(t, err)
names := make(map[string]bool)
for _, e := range entries {
names[e.Name()] = true
for _, entry := range entries {
names[entry.Name()] = true
}
assert.True(t, names["file1.txt"])
@ -302,8 +302,8 @@ func TestSqlite_List_Root_Good(t *testing.T) {
require.NoError(t, err)
names := make(map[string]bool)
for _, e := range entries {
names[e.Name()] = true
for _, entry := range entries {
names[entry.Name()] = true
}
assert.True(t, names["root.txt"])
@ -369,15 +369,15 @@ func TestSqlite_Open_Good(t *testing.T) {
require.NoError(t, sqliteMedium.Write("file.txt", "open me"))
f, err := sqliteMedium.Open("file.txt")
file, err := sqliteMedium.Open("file.txt")
require.NoError(t, err)
defer f.Close()
defer file.Close()
data, err := goio.ReadAll(f.(goio.Reader))
data, err := goio.ReadAll(file.(goio.Reader))
require.NoError(t, err)
assert.Equal(t, "open me", string(data))
stat, err := f.Stat()
stat, err := file.Stat()
require.NoError(t, err)
assert.Equal(t, "file.txt", stat.Name())
}
@ -400,14 +400,14 @@ func TestSqlite_Open_IsDirectory_Bad(t *testing.T) {
func TestSqlite_Create_Good(t *testing.T) {
sqliteMedium := newSqliteMedium(t)
w, err := sqliteMedium.Create("new.txt")
writer, err := sqliteMedium.Create("new.txt")
require.NoError(t, err)
n, err := w.Write([]byte("created"))
bytesWritten, err := writer.Write([]byte("created"))
require.NoError(t, err)
assert.Equal(t, 7, n)
assert.Equal(t, 7, bytesWritten)
err = w.Close()
err = writer.Close()
require.NoError(t, err)
content, err := sqliteMedium.Read("new.txt")
@ -420,11 +420,11 @@ func TestSqlite_Create_Overwrite_Good(t *testing.T) {
require.NoError(t, sqliteMedium.Write("file.txt", "old content"))
w, err := sqliteMedium.Create("file.txt")
writer, err := sqliteMedium.Create("file.txt")
require.NoError(t, err)
_, err = w.Write([]byte("new"))
_, err = writer.Write([]byte("new"))
require.NoError(t, err)
require.NoError(t, w.Close())
require.NoError(t, writer.Close())
content, err := sqliteMedium.Read("file.txt")
require.NoError(t, err)
@ -443,12 +443,12 @@ func TestSqlite_Append_Good(t *testing.T) {
require.NoError(t, sqliteMedium.Write("append.txt", "hello"))
w, err := sqliteMedium.Append("append.txt")
writer, err := sqliteMedium.Append("append.txt")
require.NoError(t, err)
_, err = w.Write([]byte(" world"))
_, err = writer.Write([]byte(" world"))
require.NoError(t, err)
require.NoError(t, w.Close())
require.NoError(t, writer.Close())
content, err := sqliteMedium.Read("append.txt")
require.NoError(t, err)
@ -458,12 +458,12 @@ func TestSqlite_Append_Good(t *testing.T) {
func TestSqlite_Append_NewFile_Good(t *testing.T) {
sqliteMedium := newSqliteMedium(t)
w, err := sqliteMedium.Append("new.txt")
writer, err := sqliteMedium.Append("new.txt")
require.NoError(t, err)
_, err = w.Write([]byte("fresh"))
_, err = writer.Write([]byte("fresh"))
require.NoError(t, err)
require.NoError(t, w.Close())
require.NoError(t, writer.Close())
content, err := sqliteMedium.Read("new.txt")
require.NoError(t, err)

View file

@ -99,9 +99,9 @@ func TestKeyValueMedium_List_Groups_Good(t *testing.T) {
assert.Len(t, entries, 2)
names := make(map[string]bool)
for _, e := range entries {
names[e.Name()] = true
assert.True(t, e.IsDir())
for _, entry := range entries {
names[entry.Name()] = true
assert.True(t, entry.IsDir())
}
assert.True(t, names["alpha"])
assert.True(t, names["beta"])
@ -146,11 +146,11 @@ func TestKeyValueMedium_Open_Read_Good(t *testing.T) {
keyValueMedium := newKeyValueMedium(t)
_ = keyValueMedium.Write("group/key", "hello world")
f, err := keyValueMedium.Open("group/key")
file, err := keyValueMedium.Open("group/key")
require.NoError(t, err)
defer f.Close()
defer file.Close()
data, err := io.ReadAll(f)
data, err := io.ReadAll(file)
require.NoError(t, err)
assert.Equal(t, "hello world", string(data))
}
@ -158,10 +158,10 @@ func TestKeyValueMedium_Open_Read_Good(t *testing.T) {
func TestKeyValueMedium_CreateClose_Good(t *testing.T) {
keyValueMedium := newKeyValueMedium(t)
w, err := keyValueMedium.Create("group/key")
writer, err := keyValueMedium.Create("group/key")
require.NoError(t, err)
_, _ = w.Write([]byte("streamed"))
require.NoError(t, w.Close())
_, _ = writer.Write([]byte("streamed"))
require.NoError(t, writer.Close())
value, err := keyValueMedium.Read("group/key")
require.NoError(t, err)
@ -172,10 +172,10 @@ func TestKeyValueMedium_Append_Good(t *testing.T) {
keyValueMedium := newKeyValueMedium(t)
_ = keyValueMedium.Write("group/key", "hello")
w, err := keyValueMedium.Append("group/key")
writer, err := keyValueMedium.Append("group/key")
require.NoError(t, err)
_, _ = w.Write([]byte(" world"))
require.NoError(t, w.Close())
_, _ = writer.Write([]byte(" world"))
require.NoError(t, writer.Close())
value, err := keyValueMedium.Read("group/key")
require.NoError(t, err)

View file

@ -222,7 +222,7 @@ func (service *Service) HandleWorkspaceCommand(command WorkspaceCommand) core.Re
}
// Example: result := service.HandleWorkspaceMessage(core.New(), WorkspaceCommand{Action: WorkspaceSwitchAction, WorkspaceID: "f3f0d7"})
func (service *Service) HandleWorkspaceMessage(coreInstance *core.Core, message core.Message) core.Result {
func (service *Service) HandleWorkspaceMessage(_ *core.Core, message core.Message) core.Result {
switch command := message.(type) {
case WorkspaceCommand:
return service.HandleWorkspaceCommand(command)
@ -242,11 +242,14 @@ func resolveWorkspaceHomeDirectory() string {
func joinPathWithinRoot(root string, parts ...string) (string, error) {
candidate := core.Path(append([]string{root}, parts...)...)
sep := core.Env("DS")
if sep == "" {
sep = "/"
separator := core.Env("CORE_PATH_SEPARATOR")
if separator == "" {
separator = core.Env("DS")
}
if candidate == root || core.HasPrefix(candidate, root+sep) {
if separator == "" {
separator = "/"
}
if candidate == root || core.HasPrefix(candidate, root+separator) {
return candidate, nil
}
return "", fs.ErrPermission

View file

@ -119,7 +119,7 @@ func TestService_WriteWorkspaceFile_TraversalBlocked_Bad(t *testing.T) {
}
func TestService_JoinPathWithinRoot_DefaultSeparator_Good(t *testing.T) {
t.Setenv("DS", "")
t.Setenv("CORE_PATH_SEPARATOR", "")
path, err := joinPathWithinRoot("/tmp/workspaces", "../workspaces2")
require.Error(t, err)