Files
CyberStrikeAI/internal/workflow/package/inspector_test.go
T
chenchen acfacfe1b3 feat(工作流):支持本地图编排策略包导入导出 (#195)
* docs: define local workflow package mvp

* docs: fix workflow package api contract

* feat: add local workflow package mvp

* feat(workflow): add package API client

* feat(workflow): add package import interface

* feat(workflow): connect package import flow

* fix(workflow): map package validation errors

* fix(workflow): handle import key generation errors

* feat(workflow): localize package import states

* fix(workflow): reset package import modal on open

---------

Co-authored-by: ruanmingchen <“ruanm@chenchen”>
2026-07-14 11:28:37 +08:00

155 lines
4.2 KiB
Go

package workflowpackage
import (
"archive/zip"
"bytes"
"context"
"errors"
"fmt"
"io"
"os"
"testing"
)
func TestInspectArchiveAcceptsSingleVerifiedWorkflow(t *testing.T) {
pkg, meta, err := Export(testDocument())
if err != nil {
t.Fatal(err)
}
result, err := InspectArchive(context.Background(), pkg, func(context.Context, string) error { return nil })
if err != nil {
t.Fatalf("InspectArchive: %v", err)
}
if result.PackageHash != meta.PackageHash || result.Document.ID != "web-src-hunting" {
t.Fatalf("unexpected inspection: %#v", result)
}
if result.NodeCount != 2 || result.EdgeCount != 1 {
t.Fatalf("counts = %d/%d, want 2/1", result.NodeCount, result.EdgeCount)
}
}
func TestInspectArchiveRejectsUnsafeArchiveShapes(t *testing.T) {
valid, _, err := Export(testDocument())
if err != nil {
t.Fatal(err)
}
cases := []struct {
name string
archive []byte
}{
{name: "duplicate entry", archive: appendZipEntry(t, valid, "manifest.json", []byte(`{}`), 0)},
{name: "path traversal", archive: appendZipEntry(t, valid, "../payload.json", []byte(`{}`), 0)},
{name: "symlink", archive: appendZipEntry(t, valid, "workflows/link.json", []byte("target"), 0o120777)},
{name: "undeclared file", archive: appendZipEntry(t, valid, "notes.txt", []byte("not allowed"), 0)},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
_, err := InspectArchive(context.Background(), tc.archive, func(context.Context, string) error { return nil })
if ErrorCode(err) != "WFPKG_INVALID_ARCHIVE" {
t.Fatalf("code = %q, err = %v", ErrorCode(err), err)
}
})
}
}
func TestInspectArchiveRejectsChecksumMismatchAndInvalidWorkflow(t *testing.T) {
pkg, _, err := Export(testDocument())
if err != nil {
t.Fatal(err)
}
badChecksum := replaceZipEntry(t, pkg, "checksums.sha256", []byte("00 manifest.json\n"), 0)
if _, err := InspectArchive(context.Background(), badChecksum, func(context.Context, string) error { return nil }); ErrorCode(err) != "WFPKG_CHECKSUM_MISMATCH" {
t.Fatalf("checksum code = %q, err = %v", ErrorCode(err), err)
}
if _, err := InspectArchive(context.Background(), pkg, func(context.Context, string) error { return errors.New("invalid graph") }); ErrorCode(err) != "WFPKG_WORKFLOW_INVALID" {
t.Fatalf("graph code = %q, err = %v", ErrorCode(err), err)
}
}
func appendZipEntry(t *testing.T, archive []byte, name string, data []byte, mode os.FileMode) []byte {
t.Helper()
return rewriteZip(t, archive, func(zw *zip.Writer) error {
h := &zip.FileHeader{Name: name, Method: zip.Store}
h.SetMode(mode)
w, err := zw.CreateHeader(h)
if err != nil {
return err
}
_, err = w.Write(data)
return err
})
}
func replaceZipEntry(t *testing.T, archive []byte, name string, data []byte, mode os.FileMode) []byte {
t.Helper()
zr, err := zip.NewReader(bytes.NewReader(archive), int64(len(archive)))
if err != nil {
t.Fatal(err)
}
var out bytes.Buffer
zw := zip.NewWriter(&out)
for _, f := range zr.File {
if f.Name == name {
h := &zip.FileHeader{Name: name, Method: zip.Store}
h.SetMode(mode)
w, err := zw.CreateHeader(h)
if err != nil {
t.Fatal(err)
}
if _, err := w.Write(data); err != nil {
t.Fatal(err)
}
continue
}
r, err := f.Open()
if err != nil {
t.Fatal(err)
}
h := &zip.FileHeader{Name: f.Name, Method: zip.Store}
w, err := zw.CreateHeader(h)
if err == nil {
_, err = io.Copy(w, r)
}
_ = r.Close()
if err != nil {
t.Fatal(err)
}
}
if err := zw.Close(); err != nil {
t.Fatal(err)
}
return out.Bytes()
}
func rewriteZip(t *testing.T, archive []byte, appendEntry func(*zip.Writer) error) []byte {
t.Helper()
zr, err := zip.NewReader(bytes.NewReader(archive), int64(len(archive)))
if err != nil {
t.Fatal(err)
}
var out bytes.Buffer
zw := zip.NewWriter(&out)
for _, f := range zr.File {
r, err := f.Open()
if err != nil {
t.Fatal(err)
}
h := &zip.FileHeader{Name: f.Name, Method: zip.Store}
w, err := zw.CreateHeader(h)
if err == nil {
_, err = io.Copy(w, r)
}
_ = r.Close()
if err != nil {
t.Fatal(err)
}
}
if err := appendEntry(zw); err != nil {
t.Fatal(fmt.Errorf("append entry: %w", err))
}
if err := zw.Close(); err != nil {
t.Fatal(err)
}
return out.Bytes()
}