diff --git a/internal/runner/runner.go b/internal/runner/runner.go index 510f1faac..fb9ce7484 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -802,6 +802,11 @@ func (r *Runner) RunEnumeration() error { if err := store.Load(); err != nil { return err } + // Each template is parsed once per scan, so the compiled cache only holds + // pre-compilation snapshots that nothing reads after loading. + if parser, ok := r.parser.(*templates.Parser); ok { + parser.PurgeCompiled() + } if profiles != nil { profiles.Prepare(slices.Concat(store.Templates(), store.Workflows())) executorOpts.TargetScope = profiles diff --git a/pkg/templates/compile.go b/pkg/templates/compile.go index dffeb29fb..59c8855ce 100644 --- a/pkg/templates/compile.go +++ b/pkg/templates/compile.go @@ -231,25 +231,23 @@ func parseFromSource(filePath string, preprocessor Preprocessor, options *protoc } template.Path = filePath - if !options.DoNotCache { - if cacheableTemplate == nil { - cacheableTemplate = template - } + if !options.DoNotCache && cacheableTemplate != nil { parser.compiledTemplatesCache.StoreWithoutRaw(filePath, cacheSafeCompiledTemplate(cacheableTemplate), err) } return template, nil } -// cacheSafeCompiledTemplate removes execution-scoped state before storing a -// compiled template in the shared cache. +// cacheSafeCompiledTemplate removes execution-scoped state from the +// pre-compilation snapshot before storing it in the shared cache. Its requests +// are uncompiled clones that every cache hit clones and compiles again, so they +// carry no options of their own. func cacheSafeCompiledTemplate(template *Template) *Template { tplCopy := *template tplCopy.Executer = nil tplCopy.CompiledWorkflow = nil tplCopy.Workflows = cloneWorkflowDefinitions(template.Workflows) tplCopy.Options = cacheSafeExecutorOptions(template.Options) - updateRequestOptions(&tplCopy) return &tplCopy } diff --git a/pkg/templates/compile_test.go b/pkg/templates/compile_test.go index e9d30aa3a..2b4c58238 100644 --- a/pkg/templates/compile_test.go +++ b/pkg/templates/compile_test.go @@ -741,8 +741,7 @@ func TestParseCompiledCacheDoesNotRetainExecutionOptions(t *testing.T) { require.Nil(t, cached.Options.Catalog) require.Nil(t, cached.Options.AuthProvider) require.Empty(t, cached.Options.TemporaryDirectory) - require.Nil(t, cached.RequestsHTTP[0].Options().Output) - require.Nil(t, cached.RequestsHTTP[0].Options().RateLimiter) + require.Nil(t, cached.RequestsHTTP[0].Options()) secondOutput := testutils.NewMockOutputWriter(firstOptions.Options.OmitTemplate) secondOptions := executerOpts.Copy()