Cache Terraform HCL parses during line detection#228
Conversation
|
🎯 Code Coverage (details) 🔗 Commit SHA: d1bdfd7 | Docs | Datadog PR Page | Give us feedback! |
c2cc25c to
2cff01a
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2cff01a3b1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if cached, ok := d.hclCache.Load(filePath); ok { | ||
| return cached.(*hclsyntax.Body), nil |
There was a problem hiding this comment.
Reset the Terraform parse cache for each scan
When the same Inspector/DetectKindLine is reused for a later Inspect call over the same path after the file content changes, this cache hit returns an AST parsed from the previous content because the key is only filePath and the cache is never cleared. The search logic still uses the current OriginalData/LinesOriginalData, so locateTerraformBlock can report stale block ranges or even panic while slicing the new lines if the old cached block extends past the new file. Clear this cache per Inspect call or include a per-file identity/content hash in the key.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
As currently designed, there is no path in the codebase today where stale cache entries can be read. The cache in DetectKindLine starts empty for every scan, populated only within that scan's single Inspect() call, then discarded when the Inspector is GC'd
|
I think this cache is a good local improvement, but it looks like we are still parsing Terraform twice in the full scan pipeline. The first parse happens during ingestion in file, diagnostics := hclsyntax.ParseConfig(resolved, filepath.Base(path), hcl.Pos{Byte: 0, Line: 1, Column: 1})That parsed fc, parseErr := p.convertFunc(ctx, file, inputVariables)
json, err := addExtraInfo(ctx, []model.Document{fc}, path)After Document: preparedDocument,
LineInfoDocument: document,
OriginalData: documents.Content,
LinesOriginalData: utils.SplitLines(documents.Content),Later, when building vulnerability line/block locations, block.TypeRange.Start
block.Body.SrcRange.End
attr.SrcRange.Start
attr.SrcRange.EndAttribute ranges are also used when finding nested structures at Those ranges are not available from Could we investigate a one-time parse design? For example, the Terraform parser could retain/share the parsed A few things to be careful about:
So the current PR is useful, but it may be worth exploring whether the first Terraform parse can be preserved and reused for block/remediation location detection. |
…igh-volume rules avoid re-parsing the same source on every finding.
c5419cd to
0e1f000
Compare
|
I checked the server mode path for the Terraform HCL cache lifetime. Current behavior looks safe. Even though
So with the current architecture, the Terraform HCL cache is effectively scoped to one analyze request / one scan. A second server analyze request with the same file path but changed content should not see a stale That said, I think we should add a regression test in A good test would:
This matters for future server optimizations. Keeping the inspector or rules warm between requests would be attractive for latency, but with the current path-only cache key it could make stale parses possible unless the cache is also reset or keyed by content. Side note, not mandatory for this PR: I am also a little worried about the memory footprint. The cache stores one parsed HCL body per Terraform file for the duration of the scan/request. For large repos this may be fine, but a lightweight metric or debug log could be useful later, for example number of cached HCL bodies, cache hit/miss count, and maybe total source bytes represented. That would help us validate the CPU win against the memory cost. |
…consecutive analyze requests.
d317044 to
d1bdfd7
Compare
Thanks for the review. On the ingestion-sharing idea (reusing the parser's HCL AST instead of reparsing in the detector): I prototyped it and benchmarked all three variants on
The lazy detector-side cache ( So I dropped the ingestion-sharing commit and kept the original logic. The ingestion sharing is logical if the bottleneck is “we parse every file twice.” After the lazy cache, the bottleneck is gone; what’s left is “we parse files-with-findings one extra time on the first finding,” which is cheap. Paying to retain every file’s AST up front buys almost no CPU and costs a lot of RAM. On the optional hit/miss/byte telemetry, agree it would be useful for operational visibility; happy to follow up separately if we want it. |
Motivation
Terraform line detection calls
hclsyntax.ParseConfigonce per finding to locate resource blocks. Rules that fire thousands of times on the same files (for example team-tag checks) spend most of their decode time re-parsing identical.tfsources.Changes
Terraform detector
Add a per-scan
sync.MaponDetectKindLinethat caches parsed*hclsyntax.Bodyby file path.locateTerraformBlocknow receives a pre-parsed body instead of parsing on every call.Inspector
Register the Terraform detector as a pointer so the cache is not copied across calls.
Author Checklist
QA Instruction
go test ./pkg/detector/terraform/...Blast Radius
CLI scan decode path for Terraform findings only. No change to Rego evaluation, fingerprints, or non-Terraform platforms.
Additional Notes
I submit this contribution under the Apache-2.0 license.