mirror of
https://github.com/safishamsi/graphify.git
synced 2026-10-13 23:49:14 +00:00
fix(terraform): redact secret-named variable defaults and output values
A variable or output names its secret in the block label, so the literal
sits under the generic key default/value and the key-name check never
flags it: variable "db_password" { default = "..." } reached graph.json
verbatim. Redact default/value when the label matches the sensitive-key
pattern or the block sets Terraform's own sensitive = true.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
4c735618f3
commit
01e417166b
2 files changed
+60
No files matched your search
@@ -432,6 +432,17 @@ def extract_terraform(path: Path) -> dict:
|
||||
continue
|
||||
if blk_body is not None:
|
||||
attrs = _collect_direct_attributes(blk_body)
|
||||
# A variable/output names its secret in the block LABEL, so the
|
||||
# literal sits under a generic key (`default` / `value`) that the
|
||||
# key-name check never flags. Redact it when the label names a
|
||||
# secret or the block carries Terraform's own `sensitive = true`.
|
||||
if btype in ("variable", "output") and (
|
||||
_SENSITIVE_KEY_RE.search(labels[0])
|
||||
or attrs.get("sensitive") in (True, "true")
|
||||
):
|
||||
for secret_key in ("default", "value"):
|
||||
if secret_key in attrs:
|
||||
attrs[secret_key] = _REDACTED
|
||||
if attrs:
|
||||
nodes_by_id[owner]["attributes"] = attrs
|
||||
_collect_refs(blk_body, owner, "references")
|
||||
|
||||
@@ -400,3 +400,52 @@ resource "aws_x" "y" {
|
||||
import json as _json
|
||||
assert "leaky-in-list" not in _json.dumps(node)
|
||||
assert "tok-in-list" not in _json.dumps(node)
|
||||
|
||||
|
||||
def test_terraform_secret_named_variable_default_and_output_value_are_redacted(tmp_path):
|
||||
"""A `variable`/`output` block names its secret in the block LABEL, not in an
|
||||
attribute key: `variable "db_password" { default = "..." }` stores the
|
||||
credential under the generic key `default`, and `output "admin_token"` under
|
||||
`value`. Key-name matching alone never sees the secret signal, so the
|
||||
hardcoded default reached graph.json verbatim. Terraform's own
|
||||
`sensitive = true` marker must be honoured the same way."""
|
||||
body = """\
|
||||
variable "db_password" {
|
||||
type = string
|
||||
default = "hunter2-default"
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
type = string
|
||||
default = "us-east-1"
|
||||
}
|
||||
|
||||
variable "signing_material" {
|
||||
type = string
|
||||
sensitive = true
|
||||
default = "marked-sensitive"
|
||||
}
|
||||
|
||||
output "admin_token" {
|
||||
value = "tok-in-output"
|
||||
}
|
||||
|
||||
output "bucket_name" {
|
||||
value = "my-bucket"
|
||||
}
|
||||
"""
|
||||
r = extract_terraform(_write(tmp_path, "vars.tf", body))
|
||||
by_label = {n["label"]: n for n in r["nodes"]}
|
||||
# secret-named blocks: the literal is redacted, the type stays visible
|
||||
assert by_label["var.db_password"]["attributes"]["default"] == "[redacted]"
|
||||
assert by_label["var.db_password"]["attributes"]["type"] == "string"
|
||||
assert by_label["output.admin_token"]["attributes"]["value"] == "[redacted]"
|
||||
# Terraform's explicit `sensitive = true` marker is honoured
|
||||
assert by_label["var.signing_material"]["attributes"]["default"] == "[redacted]"
|
||||
# ordinary variables/outputs are untouched
|
||||
assert by_label["var.region"]["attributes"]["default"] == "us-east-1"
|
||||
assert by_label["output.bucket_name"]["attributes"]["value"] == "my-bucket"
|
||||
import json as _json
|
||||
dumped = _json.dumps(r["nodes"])
|
||||
for secret in ("hunter2-default", "tok-in-output", "marked-sensitive"):
|
||||
assert secret not in dumped
|
||||
Reference in new issue
Block a user