Skip to content

Preserve Terraform attributes during extraction - #3644

Open
hopstreax wants to merge 1 commit into
Graphify-Labs:v8from
hopstreax:investigate/3625-terraform-attributes
Open

hopstreax wants to merge 1 commit into
Graphify-Labs:v8from
hopstreax:investigate/3625-terraform-attributes

Conversation

@hopstreax

Copy link
Copy Markdown
Contributor

Summary

Fixes #3625 by preserving direct Terraform/HCL attributes on their owning block nodes and exposing them to Graphify's search/query context.

Changes

  • Extract direct Terraform attributes into a top-level attributes mapping on resource/data/module/variable/output/provider nodes.
  • Decode primitive literals and simple collections into JSON-compatible values.
  • Preserve non-literal/reference expressions as bounded source text while keeping existing references and depends_on edges unchanged.
  • Make attribute names and values available to the existing lexical search pipeline.
  • Include bounded attribute context in subgraph/node rendering.
  • Add a named security bound for the maximum number of attributes per block.
  • Add regression coverage for literals, collections, expressions, nested-block isolation, search discovery, rendering, and serialization round-trips.

Validation

  • pytest tests/test_terraform.py — 15 passed
  • pytest tests/test_serve.py tests/test_build.py tests/test_dedup.py — 343 passed
  • git diff --check — clean

The existing Windows-only path-separator failure in tests/test_terraform_modules.py remains unrelated to this change.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 1 advisory finding(s) below merit a look before merge.

Formal verification. 2 change(s) tested, no difference found (not proven).


Graphify review — findings

Extracts direct block attributes during Terraform parsing, recording bounded primitive/list/object literals under each node's attributes key via _collect_direct_attributes and _parse_attr_value, with control-char stripping and caps for value length, list items, and attribute count (_METADATA_MAX_ATTRIBUTES). Makes the serve layer search and surface those attributes: _node_attributes_text flattens them into normalized/tokenized search fields, _score_query awards a new _ATTRIBUTE_MATCH_BONUS tier for term hits, and node detail and subgraph rendering emit truncated, sanitized attribute summaries.

Worth a look

  • Subgraph context emits sensitive attribute valuesgraphify/serve.py:1146 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 2601 functions depend on the 214 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 645 callers, 45 callees
  • new: _rebuild_code() — 129 callers, 54 callees
  • new: build_merge() — 76 callers, 14 callees
  • new: _extract_generic() — 18 callers, 29 callees
  • new: extract_bash() — 45 callers, 10 callees
  • new: to_json() — 58 callers, 7 callees
  • new: extract_js() — 85 callers, 4 callees
  • new: extract_xaml() — 19 callers, 17 callees
  • …and 56 more — each is listed as a finding

Verification — 2601 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 1358 function(s) in the blast radius were not formally verified this run

Test selection

Test selection

69 of 286 test file(s) selected (24%) via static blast radius.

  • tests/test_astro_extraction.py — impact
  • tests/test_atomic_writes.py — impact
  • tests/test_benchmark.py — impact
  • tests/test_benchmark_raw_graph.py — impact
  • tests/test_build.py — impact
  • tests/test_build_merge_dedup_scope.py — impact
  • tests/test_build_merge_hyperedges_and_prune.py — impact
  • tests/test_build_merge_shrink_guard.py — impact
  • tests/test_callflow_html.py — impact
  • tests/test_carried_hyperedge_remap.py — impact
  • tests/test_cjs_module_extension.py — impact
  • tests/test_cli_export.py — impact
  • tests/test_community_labels_skill.py — impact
  • tests/test_confidence.py — impact
  • tests/test_corrupt_graph_json.py — impact
  • tests/test_cpp_nested_and_cli.py — impact
  • tests/test_cross_extension_reexport_self_cycle.py — impact
  • tests/test_dotnet.py — impact
  • tests/test_export.py — impact
  • tests/test_export_control_characters.py — impact
  • tests/test_extract.py — impact
  • tests/test_file_label_disambiguation.py — impact
  • tests/test_global_add_tag_inference.py — impact
  • tests/test_global_graph.py — impact
  • tests/test_go_qualified_resolution.py — impact
  • tests/test_god_nodes_exclude_hubs.py — impact
  • tests/test_hyperedge_roundtrip.py — impact
  • tests/test_hypergraph.py — impact
  • tests/test_import_extension_resolution.py — impact
  • tests/test_indirect_dispatch.py — impact
  • tests/test_indirect_dispatch_assign_return.py — impact
  • tests/test_indirect_dispatch_getattr.py — impact
  • tests/test_ingest.py — impact
  • tests/test_issue_3472_source_file_collision.py — impact
  • tests/test_js_exported_scalar_bindings.py — impact
  • tests/test_labeling.py — impact
  • tests/test_languages.py — impact
  • tests/test_markdown_code_spans.py — impact
  • tests/test_mcp_ingest.py — impact
  • tests/test_multigraph_diagnostics.py — impact
  • tests/test_multilang.py — impact
  • tests/test_paths.py — impact
  • tests/test_pipeline.py — impact
  • tests/test_prs.py — impact
  • tests/test_prune_sweeps_orphans.py — impact
  • tests/test_python_underscore_resolution.py — impact
  • tests/test_query_induced_edges.py — impact
  • tests/test_query_mcp_direction.py — impact
  • tests/test_query_names_its_graph.py — impact
  • tests/test_rationale.py — impact
  • … and 19 more

Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.

Formal verification

Could not verify: Could not verify extract\_terraform.

The verifier did not have enough to check extract\_terraform, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: no capturable inputs from the test suite; property tier: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify \_build\_server.

The verifier did not have enough to check \_build\_server, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: no capturable inputs from the test suite; property tier: not verifiable: all 23 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous

No difference found (not proven): No behavior difference found in \_node\_search\_text (not a proof).

The verifier ran both versions of \_node\_search\_text on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

Could not verify: Could not verify \_score\_query.

The verifier did not have enough to check \_score\_query, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: no capturable inputs from the test suite; property tier: not verifiable: all 45 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly TypeError — names the real obstacle, not a sampling gap)

No difference found (not proven): No behavior difference found in \_subgraph\_to\_text (not a proof).

The verifier ran both versions of \_subgraph\_to\_text on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

· 64 more finding(s) on lines outside this diff (see the check run).

safishamsi added a commit that referenced this pull request Sep 18, 2026
…the graph (#3644 follow-up)

#3644 persists HCL block attributes into node metadata, which is serialized to
graph.json verbatim and surfaced to the model over MCP (query/get_node) with no
sanitize_metadata pass. A hardcoded credential in a .tf file (password,
db_password, aws_secret_access_key, client_secret, a connection string, ...)
would leak. Redact the VALUE of any attribute whose key names a secret while
keeping the key visible, so instance_type/ami queries still work and a user can
still see THAT a secret is set. Recurses into map values so a nested secret in a
tags/connection map is redacted too.

Also notes Terraform block attributes in the _AST_CACHE_SCHEMA 4 comment (they
are newly-cached facts covered by the same bump #3653 introduced).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Terraform/HCL: attribute names and values are discarded, so attribute-level questions are unanswerable (and query answers them with unrelated noise)

1 participant