From 0677223ff668849013d42f3d90b92d5e1f092377 Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Sat, 23 May 2026 12:24:26 +0100 Subject: [PATCH] fix(apply): never substitute env vars - leave ${VAR} for harnesses to resolve --- internal/apply/apply.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/internal/apply/apply.go b/internal/apply/apply.go index a9b8cde..a099afe 100644 --- a/internal/apply/apply.go +++ b/internal/apply/apply.go @@ -12,7 +12,6 @@ import ( "github.com/lukaszraczylo/harness-sync/internal/canonical" "github.com/lukaszraczylo/harness-sync/internal/gitx" "github.com/lukaszraczylo/harness-sync/internal/merge" - "github.com/lukaszraczylo/harness-sync/internal/secrets" ) // Options configures a Run call. @@ -42,12 +41,14 @@ type Action struct { // Run renders all adapters and writes their FileSets to disk. // Conflicts are recorded as .rej files; processing continues past conflicts. -// Run renders all adapters and writes their FileSets to disk. -// Conflicts are recorded as .rej files; processing continues past conflicts. +// +// IMPORTANT: harness-sync intentionally does NOT resolve ${VAR} references in +// canonical configs. Every supported harness performs its own env-var +// expansion at MCP launch / config-load time, so resolving here would only +// bake real secret values into the rendered target files AND the state/ +// snapshots tracked by git — which is a security regression. Leave +// placeholders alone; the downstream harness resolves them at use time. func Run(opt Options) (*Report, error) { - if err := canonical.SubstituteSecrets(opt.Bundle, secrets.OSEnv); err != nil { - return nil, fmt.Errorf("env-var substitution: %w", err) - } rep := &Report{} for _, ad := range opt.Adapters { files, err := ad.Render(opt.Bundle)