From 68414987d5de213fcab997edf3f69e9c84d80960dd714e7832a683d5a1b3b282 Mon Sep 17 00:00:00 2001 From: Tyler J King Date: Wed, 13 May 2026 08:43:05 -0400 Subject: [PATCH] fix(gsap-attestor): handle SPIRE's HCL v1 quoted-key format SPIRE converts JSON plugin_data to HCL v1 native syntax with quoted attribute names ("max_depth" = 10). HCL v2's parser rejects quoted keys, so strip them before parsing. Co-Authored-By: Claude Opus 4.6 --- cmd/gsap-attestor/attestor_test.go | 28 ++++++++++++++++++++++++++++ cmd/gsap-attestor/main.go | 20 +++++++++++--------- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/cmd/gsap-attestor/attestor_test.go b/cmd/gsap-attestor/attestor_test.go index 1468d02..5c210a3 100644 --- a/cmd/gsap-attestor/attestor_test.go +++ b/cmd/gsap-attestor/attestor_test.go @@ -367,6 +367,34 @@ func TestAttest_EndToEnd_CapabilityMask(t *testing.T) { assertSelector(t, smap, "capability_mask", "0x0f") } +// --- decodeConfig tests --- + +func TestDecodeConfig_SPIREFormat(t *testing.T) { + // SPIRE converts JSON plugin_data to HCL v1 native syntax with quoted keys + input := "\"max_depth\" = 10\n\n\"proc_root\" = \"/proc\"" + cfg, err := decodeConfig(input) + if err != nil { + t.Fatalf("decodeConfig failed on SPIRE format: %v", err) + } + if cfg.MaxDepth != 10 { + t.Errorf("max_depth = %d, want 10", cfg.MaxDepth) + } + if cfg.ProcRoot != "/proc" { + t.Errorf("proc_root = %q, want /proc", cfg.ProcRoot) + } +} + +func TestDecodeConfig_NativeHCL(t *testing.T) { + input := "max_depth = 10\nproc_root = \"/proc\"" + cfg, err := decodeConfig(input) + if err != nil { + t.Fatalf("decodeConfig failed on native HCL: %v", err) + } + if cfg.MaxDepth != 10 || cfg.ProcRoot != "/proc" { + t.Errorf("unexpected config: %+v", cfg) + } +} + // --- test helpers --- func selectorMap(selectors []string) map[string]string { diff --git a/cmd/gsap-attestor/main.go b/cmd/gsap-attestor/main.go index 7598344..d23753b 100644 --- a/cmd/gsap-attestor/main.go +++ b/cmd/gsap-attestor/main.go @@ -2,7 +2,7 @@ package main import ( "context" - "fmt" + "regexp" "sync" "github.com/hashicorp/hcl/v2/hclsimple" @@ -40,17 +40,19 @@ func (p *Plugin) Attest(ctx context.Context, req *workloadattestorv1.AttestReque }, nil } +// unquoteHCLKeys strips quotes from HCL v1-style attribute names +// (e.g. `"max_depth" = 10` → `max_depth = 10`). SPIRE converts JSON +// plugin_data to HCL v1 native syntax which uses quoted keys that +// HCL v2's parser rejects. +var hclQuotedKey = regexp.MustCompile(`(?m)^(\s*)"([a-zA-Z_][a-zA-Z0-9_]*)"(\s*=)`) + func decodeConfig(data string) (GsapAttestorConfig, error) { var cfg GsapAttestorConfig - jsonErr := hclsimple.Decode("plugin.json", []byte(data), nil, &cfg) - if jsonErr == nil { - return cfg, nil + normalized := hclQuotedKey.ReplaceAllString(data, `${1}${2}${3}`) + if err := hclsimple.Decode("plugin.hcl", []byte(normalized), nil, &cfg); err != nil { + return cfg, err } - hclErr := hclsimple.Decode("plugin.hcl", []byte(data), nil, &cfg) - if hclErr == nil { - return cfg, nil - } - return cfg, fmt.Errorf("json: %v; hcl: %v; raw input: %q", jsonErr, hclErr, data) + return cfg, nil } func (p *Plugin) Configure(_ context.Context, req *configv1.ConfigureRequest) (*configv1.ConfigureResponse, error) {