feat: add deployment foundation and cross-device handoff
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func packageFixture(t *testing.T) (string, string) {
|
||||
return packageFixturePayload(t, []byte("services: {}\n"))
|
||||
}
|
||||
|
||||
func packageFixturePayload(t *testing.T, payload []byte) (string, string) {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
digest := func(data []byte) string { sum := sha256.Sum256(data); return "sha256:" + hex.EncodeToString(sum[:]) }
|
||||
manifest := map[string]any{
|
||||
"protocolVersion": 1, "appId": "example", "version": "1.0.0", "runtime": "compose", "entrypoint": "compose.yaml",
|
||||
"platforms": []string{"linux/amd64"},
|
||||
"components": []map[string]string{{"name": "server", "image": "example/server@sha256:" + strings.Repeat("a", 64)}},
|
||||
"files": []map[string]string{{"path": "compose.yaml", "digest": digest(payload)}},
|
||||
}
|
||||
raw, err := json.Marshal(manifest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(dir, "manifest.json"), raw, 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(dir, "compose.yaml"), payload, 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return dir, digest(raw)
|
||||
}
|
||||
|
||||
func TestCheckPackagePolicyCLI(t *testing.T) {
|
||||
payload := `{"services":{"server":{"image":"example/server@sha256:` + strings.Repeat("a", 64) + `","user":"1000:1000","read_only":true,"cap_drop":["ALL"],"security_opt":["no-new-privileges:true"],"restart":"no","networks":["backend"],"volumes":[]}},"networks":{"backend":{"internal":true}},"volumes":{}}`
|
||||
for _, tc := range []struct {
|
||||
payload string
|
||||
accepted bool
|
||||
}{
|
||||
{payload, true},
|
||||
{strings.Replace(payload, `"read_only":true`, `"read_only":false`, 1), false},
|
||||
{`services: {}`, false},
|
||||
} {
|
||||
dir, digest := packageFixturePayload(t, []byte(tc.payload))
|
||||
input, _ := json.Marshal(map[string]string{"directory": dir, "expectedDigest": digest})
|
||||
code, out, diagnostics := run([]string{"check-package"}, string(input))
|
||||
if tc.accepted {
|
||||
var result struct {
|
||||
PolicyPassed bool `json:"policyPassed"`
|
||||
Executable bool `json:"executable"`
|
||||
Profile string `json:"profile"`
|
||||
Digest string `json:"digest"`
|
||||
}
|
||||
if code != 0 || json.Unmarshal([]byte(out), &result) != nil || !result.PolicyPassed || result.Executable || result.Profile == "" || result.Digest != digest {
|
||||
t.Fatalf("bad check result: %s %s", out, diagnostics)
|
||||
}
|
||||
if strings.Contains(out, "1000:1000") {
|
||||
t.Fatal("payload leaked")
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(dir, "compose.yaml"), []byte(tc.payload+" "), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if code, _, _ := run([]string{"check-package"}, string(input)); code == 0 {
|
||||
t.Fatal("policy bypassed integrity check")
|
||||
}
|
||||
} else if code == 0 || out != "" {
|
||||
t.Fatal("unsafe package accepted")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyPackageCLI(t *testing.T) {
|
||||
dir, digest := packageFixture(t)
|
||||
input, _ := json.Marshal(map[string]string{"directory": dir, "expectedDigest": digest})
|
||||
code, out, diagnostics := run([]string{"verify-package"}, string(input))
|
||||
if code != 0 || diagnostics != "" {
|
||||
t.Fatalf("package verification failed: %s", diagnostics)
|
||||
}
|
||||
var response struct {
|
||||
Verified bool `json:"verified"`
|
||||
Executable bool `json:"executable"`
|
||||
PublisherAuthenticated bool `json:"publisherAuthenticated"`
|
||||
FileCount int `json:"fileCount"`
|
||||
Digest string `json:"digest"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(out), &response); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !response.Verified || response.Executable || response.PublisherAuthenticated || response.FileCount != 1 || response.Digest != digest {
|
||||
t.Fatalf("misleading verification: %s", out)
|
||||
}
|
||||
if strings.Contains(out, "services: {}") {
|
||||
t.Fatal("file contents leaked")
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(dir, "compose.yaml"), []byte("secret tampering"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, out, diagnostics = run([]string{"verify-package"}, string(input))
|
||||
if code == 0 || out != "" || strings.Contains(diagnostics, "secret tampering") {
|
||||
t.Fatal("tampering accepted or leaked")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyPackageInvalidRequests(t *testing.T) {
|
||||
for _, input := range []string{`{}`, `{"directory":".","expectedDigest":"latest"}`, `{"directory":"secret","expectedDigest":"bad","password":"do-not-echo"}`} {
|
||||
code, out, diagnostics := run([]string{"verify-package"}, input)
|
||||
if code == 0 || out != "" || strings.Contains(diagnostics, "do-not-echo") {
|
||||
t.Fatal("invalid package request accepted or leaked")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user