mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-07-16 00:47:29 +02:00
acfacfe1b3
* 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”>
155 lines
4.2 KiB
Go
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()
|
|
}
|