mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-22 11:10:12 +00:00
test(agentcompat): add integration scenarios
Co-authored-by: naiba/CloudCode <hi+cloudcode@nai.ba>
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
//go:build linux
|
||||
|
||||
package scenario
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/nezhahq/nezha/integration/agentcompat/internal/fixture"
|
||||
)
|
||||
|
||||
func verifyMCPFilesystemPathGuards(ctx context.Context, assertions *AssertionSet, filesystem mcpFilesystemClient, fixtureParent string) error {
|
||||
directTarget := filepath.Join(fixtureParent, "outside-sentinel")
|
||||
symlinkParentTarget := filepath.Join(fixtureParent, "outside-directory", "parent-target.txt")
|
||||
symlinkFinalTarget := filepath.Join(fixtureParent, "final-target.txt")
|
||||
if err := os.Mkdir(filepath.Dir(symlinkParentTarget), 0o700); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, target := range []string{directTarget, symlinkParentTarget, symlinkFinalTarget} {
|
||||
if err := os.WriteFile(target, []byte("unchanged:"+filepath.Base(target)), 0o600); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := os.Symlink(filepath.Dir(symlinkParentTarget), filepath.Join(filesystem.root.Absolute(), "linked-parent")); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Symlink(symlinkFinalTarget, filepath.Join(filesystem.root.Absolute(), "linked-final")); err != nil {
|
||||
return err
|
||||
}
|
||||
before, err := filesystemTargetHashes(directTarget, symlinkParentTarget, symlinkFinalTarget)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
requestsBefore := filesystem.client.RequestCount()
|
||||
rejections := []struct {
|
||||
want fixture.PathRejectionReason
|
||||
run func() error
|
||||
}{
|
||||
{fixture.PathRejectionAbsolute, func() error { _, err := filesystem.read(ctx, directTarget, 0, 1, "utf8"); return err }},
|
||||
{fixture.PathRejectionParent, func() error {
|
||||
_, err := filesystem.write(ctx, mcpFilesystemWrite{relative: "../outside-sentinel", content: "changed", encoding: "utf8", mode: "0600"})
|
||||
return err
|
||||
}},
|
||||
{fixture.PathRejectionVolume, func() error { _, err := filesystem.list(ctx, `C:\outside.txt`, false); return err }},
|
||||
{fixture.PathRejectionSeparator, func() error { _, err := filesystem.read(ctx, `inside\outside.txt`, 0, 1, "utf8"); return err }},
|
||||
{fixture.PathRejectionDestructiveRoot, func() error { _, err := filesystem.delete(ctx, ".", true); return err }},
|
||||
{fixture.PathRejectionSymlinkParent, func() error {
|
||||
_, err := filesystem.write(ctx, mcpFilesystemWrite{relative: "linked-parent/parent-target.txt", content: "changed", encoding: "utf8", mode: "0600"})
|
||||
return err
|
||||
}},
|
||||
{fixture.PathRejectionSymlinkFinal, func() error { _, err := filesystem.delete(ctx, "linked-final", false); return err }},
|
||||
}
|
||||
matched := 0
|
||||
for _, rejection := range rejections {
|
||||
var pathError *fixture.AgentPathError
|
||||
if errors.As(rejection.run(), &pathError) && pathError.Reason == rejection.want {
|
||||
matched++
|
||||
}
|
||||
}
|
||||
requestsAfter := filesystem.client.RequestCount()
|
||||
dispatched := int64(requestsAfter) - int64(requestsBefore)
|
||||
after, err := filesystemTargetHashes(directTarget, symlinkParentTarget, symlinkFinalTarget)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := errors.Join(
|
||||
os.Remove(filepath.Join(filesystem.root.Absolute(), "linked-parent")),
|
||||
os.Remove(filepath.Join(filesystem.root.Absolute(), "linked-final")),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
assertions.Record("fixture path rejections dispatch zero MCP HTTP requests", matched == len(rejections) && dispatched == 0, fmt.Sprintf("path_rejections_dispatched: %d; matched=%d total=%d requests_before=%d requests_after=%d", dispatched, matched, len(rejections), requestsBefore, requestsAfter))
|
||||
assertions.Record("fixture path rejections leave outside and symlink targets unchanged", before == after, fmt.Sprintf("before=%x after=%x", before, after))
|
||||
return nil
|
||||
}
|
||||
|
||||
func filesystemTargetHashes(paths ...string) ([32]byte, error) {
|
||||
hash := sha256.New()
|
||||
for _, path := range paths {
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return [32]byte{}, err
|
||||
}
|
||||
if _, err := hash.Write(content); err != nil {
|
||||
return [32]byte{}, err
|
||||
}
|
||||
}
|
||||
var digest [32]byte
|
||||
copy(digest[:], hash.Sum(nil))
|
||||
return digest, nil
|
||||
}
|
||||
Reference in New Issue
Block a user