Compare commits

..

No commits in common. "4eb4bcfc507e498952352a000942eb8bbe6a6605" and "f074d71cf9bd9a6d94384f5205a59de9b8876740" have entirely different histories.

5 changed files with 11 additions and 81 deletions

View File

@ -1107,29 +1107,12 @@ func writeMod(path string, m *core.ModFile) error {
} }
fmt.Fprintf(&b, "tags = [%s]\n", strings.Join(quoted, ", ")) fmt.Fprintf(&b, "tags = [%s]\n", strings.Join(quoted, ", "))
} }
if len(m.Plugin.RequiredIconPacks) > 0 {
quoted := make([]string, len(m.Plugin.RequiredIconPacks))
for i, p := range m.Plugin.RequiredIconPacks {
quoted[i] = fmt.Sprintf("%q", p)
}
fmt.Fprintf(&b, "required_icon_packs = [%s]\n", strings.Join(quoted, ", "))
}
if m.Plugin.Private { if m.Plugin.Private {
b.WriteString("private = true\n") b.WriteString("private = true\n")
} }
if m.Plugin.DataDir { if m.Plugin.DataDir {
b.WriteString("data_dir = true\n") b.WriteString("data_dir = true\n")
} }
if len(m.Plugin.AllowedHosts) > 0 {
quoted := make([]string, len(m.Plugin.AllowedHosts))
for i, h := range m.Plugin.AllowedHosts {
quoted[i] = fmt.Sprintf("%q", h)
}
fmt.Fprintf(&b, "allowed_hosts = [%s]\n", strings.Join(quoted, ", "))
}
if m.Plugin.MaxResponseMB > 0 {
fmt.Fprintf(&b, "max_response_mb = %d\n", m.Plugin.MaxResponseMB)
}
if m.Compatibility != nil { if m.Compatibility != nil {
b.WriteString("\n[compatibility]\n") b.WriteString("\n[compatibility]\n")
fmt.Fprintf(&b, "block_core = %q\n", m.Compatibility.BlockCore) fmt.Fprintf(&b, "block_core = %q\n", m.Compatibility.BlockCore)

View File

@ -364,45 +364,6 @@ func TestWriteMod_PrivateFalseOmitted(t *testing.T) {
} }
} }
// TestWriteMod_RoundTripsFirstClassFields guards the writeMod ⇄ ParseModFull
// round-trip for the first-class fields the hand-rolled writer must emit: a
// bump/init/tags edit re-serializes plugin.mod, and any field the writer forgets
// is silently dropped. allowed_hosts (ADR 0023) dropping is a security-relevant
// regression — a bump strips the egress declaration and the consent gate goes
// dark — so this test exists to keep the writer in sync with core.ModFile.
func TestWriteMod_RoundTripsFirstClassFields(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "plugin.mod")
m := &core.ModFile{Plugin: core.ModPlugin{
Name: "myplugin",
Scope: "ninja",
Version: "1.2.3",
AllowedHosts: []string{"api.cal.com", "*.example.com"},
MaxResponseMB: 25,
RequiredIconPacks: []string{"tabler"},
}}
if err := writeMod(path, m); err != nil {
t.Fatalf("writeMod: %v", err)
}
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read back: %v", err)
}
back, err := core.ParseModFull(data)
if err != nil {
t.Fatalf("ParseModFull round-trip: %v\n%s", err, data)
}
if got := back.Plugin.AllowedHosts; len(got) != 2 || got[0] != "api.cal.com" || got[1] != "*.example.com" {
t.Errorf("allowed_hosts not preserved: %v\n%s", got, data)
}
if back.Plugin.MaxResponseMB != 25 {
t.Errorf("max_response_mb not preserved: %d\n%s", back.Plugin.MaxResponseMB, data)
}
if got := back.Plugin.RequiredIconPacks; len(got) != 1 || got[0] != "tabler" {
t.Errorf("required_icon_packs not preserved: %v\n%s", got, data)
}
}
func TestParsePrivateCoord(t *testing.T) { func TestParsePrivateCoord(t *testing.T) {
cases := []struct { cases := []struct {
in string in string

7
go.mod
View File

@ -5,7 +5,7 @@ go 1.26.4
require ( require (
connectrpc.com/connect v1.20.0 connectrpc.com/connect v1.20.0
git.dev.alexdunmow.com/block/core v0.18.2 git.dev.alexdunmow.com/block/core v0.18.2
git.dev.alexdunmow.com/block/pluginsdk v0.2.5 git.dev.alexdunmow.com/block/pluginsdk v0.1.0
github.com/chromedp/cdproto v0.0.0-20260321001828-e3e3800016bc github.com/chromedp/cdproto v0.0.0-20260321001828-e3e3800016bc
github.com/chromedp/chromedp v0.15.1 github.com/chromedp/chromedp v0.15.1
github.com/klauspost/compress v1.18.6 github.com/klauspost/compress v1.18.6
@ -30,7 +30,6 @@ require (
github.com/kr/text v0.2.0 // indirect github.com/kr/text v0.2.0 // indirect
github.com/spf13/pflag v1.0.9 // indirect github.com/spf13/pflag v1.0.9 // indirect
golang.org/x/mod v0.37.0 // indirect golang.org/x/mod v0.37.0 // indirect
golang.org/x/net v0.56.0 // indirect golang.org/x/sys v0.44.0 // indirect
golang.org/x/sys v0.46.0 // indirect golang.org/x/text v0.36.0 // indirect
golang.org/x/text v0.38.0 // indirect
) )

18
go.sum
View File

@ -2,8 +2,8 @@ connectrpc.com/connect v1.20.0 h1:6TNDAB+WeNd2uolWNlYczB5E0KNNaVMNUEx8JEUsPmQ=
connectrpc.com/connect v1.20.0/go.mod h1:A2ygJrukXwWy32vkCAAHNVguZrqZ+jeZ9rGRnGR4dN4= connectrpc.com/connect v1.20.0/go.mod h1:A2ygJrukXwWy32vkCAAHNVguZrqZ+jeZ9rGRnGR4dN4=
git.dev.alexdunmow.com/block/core v0.18.2 h1:+3OfZ424yoc1k1CucXYARk8TBkh8Z693HRkhf5Ci2wU= git.dev.alexdunmow.com/block/core v0.18.2 h1:+3OfZ424yoc1k1CucXYARk8TBkh8Z693HRkhf5Ci2wU=
git.dev.alexdunmow.com/block/core v0.18.2/go.mod h1:GGuUu826AoJepC/hKLGJ7BX3PQaDss9ueCT0se6Ao2w= git.dev.alexdunmow.com/block/core v0.18.2/go.mod h1:GGuUu826AoJepC/hKLGJ7BX3PQaDss9ueCT0se6Ao2w=
git.dev.alexdunmow.com/block/pluginsdk v0.2.5 h1:01d+5stAywSENScafnNKyMc2ZM5GGFt+hCBKuKTFJW4= git.dev.alexdunmow.com/block/pluginsdk v0.1.0 h1:Dgbt8Q5b+1o3HdftQ7yhV2byWI5q3exiN9oXhwz2MjM=
git.dev.alexdunmow.com/block/pluginsdk v0.2.5/go.mod h1:Z+eG+WZxAP0jfreLqlGcc0kkWKt8RWevzWyWn8d+dhM= git.dev.alexdunmow.com/block/pluginsdk v0.1.0/go.mod h1:uz6oRurbuHdTcUxg47ymVgBqRARKlpXp1JIziXevzms=
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/chromedp/cdproto v0.0.0-20260321001828-e3e3800016bc h1:wkN/LMi5vc60pBRWx6qpbk/aEvq3/ZVNpnMvsw8PVVU= github.com/chromedp/cdproto v0.0.0-20260321001828-e3e3800016bc h1:wkN/LMi5vc60pBRWx6qpbk/aEvq3/ZVNpnMvsw8PVVU=
@ -68,15 +68,13 @@ github.com/tetratelabs/wazero v1.12.0/go.mod h1:LvKtzl2RqO4gyF27BiXU+nKAjcV8f38U
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -13,7 +13,6 @@ import (
abiv1 "git.dev.alexdunmow.com/block/pluginsdk/abi/v1" abiv1 "git.dev.alexdunmow.com/block/pluginsdk/abi/v1"
core "git.dev.alexdunmow.com/block/pluginsdk/plugin" core "git.dev.alexdunmow.com/block/pluginsdk/plugin"
"git.dev.alexdunmow.com/block/pluginsdk/egress"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
) )
@ -129,16 +128,6 @@ func Build(ctx context.Context, opts BuildOptions) (*BuildResult, error) {
// grant crosses from mod → manifest; the loader then reads one source. // grant crosses from mod → manifest; the loader then reads one source.
manifest.DataDir = mod.Plugin.DataDir manifest.DataDir = mod.Plugin.DataDir
// 4a. Stamp the egress declaration (ADR 0023). Validate the host-pattern
// grammar here so a malformed allowed_hosts fails the build, not the
// install; the platform denylist is enforced host-side at publish and
// fetch (it needs the instance's own domains, which the packer lacks).
if _, err := egress.ParseAllowlist(mod.Plugin.AllowedHosts); err != nil {
return nil, fmt.Errorf("allowed_hosts: %w", err)
}
manifest.AllowedHosts = mod.Plugin.AllowedHosts
manifest.MaxResponseMb = mod.Plugin.MaxResponseMB
// 4b. Mixed-form artifacts (WO-WZ-021): a reduced wasm plugin may keep a // 4b. Mixed-form artifacts (WO-WZ-021): a reduced wasm plugin may keep a
// minimal guest for genuine logic (a contact route, Stripe) while its // minimal guest for genuine logic (a contact route, Stripe) while its
// declarative surfaces (theme presets, bundled fonts, master pages, // declarative surfaces (theme presets, bundled fonts, master pages,