From b6fc34e719b455c7a1813d7747cead13b7a88c88 Mon Sep 17 00:00:00 2001 From: Pam Selle Date: Mon, 4 Jan 2021 15:38:58 -0500 Subject: [PATCH] Mark objects with keys that are sensitive This adjusts prior behavior that would error to now allow keys that are marked; those marks are removed in evaluation in order to give a valid string for the map key, but the resulting object or tuple is marked as a whole with whatever mark the key contained, ensuring marks are maintained. --- hclsyntax/expression.go | 39 ++++++++++-------------------------- hclsyntax/expression_test.go | 39 ++++++++++++++++++++++++++++++------ 2 files changed, 44 insertions(+), 34 deletions(-) diff --git a/hclsyntax/expression.go b/hclsyntax/expression.go index 7917853..9044df0 100644 --- a/hclsyntax/expression.go +++ b/hclsyntax/expression.go @@ -788,6 +788,7 @@ func (e *ObjectConsExpr) walkChildNodes(w internalWalkFunc) { func (e *ObjectConsExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { var vals map[string]cty.Value var diags hcl.Diagnostics + var marks []cty.ValueMarks // This will get set to true if we fail to produce any of our keys, // either because they are actually unknown or if the evaluation produces @@ -825,18 +826,8 @@ func (e *ObjectConsExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics continue } - if key.IsMarked() { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Marked value as key", - Detail: "Can't use a marked value as a key.", - Subject: item.ValueExpr.Range().Ptr(), - Expression: item.KeyExpr, - EvalContext: ctx, - }) - known = false - continue - } + key, keyMarks := key.Unmark() + marks = append(marks, keyMarks) var err error key, err = convert.Convert(key, cty.String) @@ -867,7 +858,7 @@ func (e *ObjectConsExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics return cty.DynamicVal, diags } - return cty.ObjectVal(vals), diags + return cty.ObjectVal(vals).WithMarks(marks...), diags } func (e *ObjectConsExpr) Range() hcl.Range { @@ -997,6 +988,7 @@ type ForExpr struct { func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { var diags hcl.Diagnostics + var marks []cty.ValueMarks collVal, collDiags := e.CollExpr.Value(ctx) diags = append(diags, collDiags...) @@ -1018,7 +1010,8 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { } // Unmark collection before checking for iterability, because marked // values cannot be iterated - collVal, marks := collVal.Unmark() + collVal, collMarks := collVal.Unmark() + marks = append(marks, collMarks) if !collVal.CanIterateElements() { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, @@ -1188,18 +1181,8 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { continue } - if key.IsMarked() { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid object key", - Detail: "Marked values cannot be used as object keys.", - Subject: e.KeyExpr.Range().Ptr(), - Context: &e.SrcRange, - Expression: e.KeyExpr, - EvalContext: childCtx, - }) - continue - } + key, keyMarks := key.Unmark() + marks = append(marks, keyMarks) val, valDiags := e.ValExpr.Value(childCtx) diags = append(diags, valDiags...) @@ -1239,7 +1222,7 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { } } - return cty.ObjectVal(vals).WithMarks(marks), diags + return cty.ObjectVal(vals).WithMarks(marks...), diags } else { // Producing a tuple @@ -1315,7 +1298,7 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { return cty.DynamicVal, diags } - return cty.TupleVal(vals).WithMarks(marks), diags + return cty.TupleVal(vals).WithMarks(marks...), diags } } diff --git a/hclsyntax/expression_test.go b/hclsyntax/expression_test.go index 89805fc..25a5ff3 100644 --- a/hclsyntax/expression_test.go +++ b/hclsyntax/expression_test.go @@ -516,8 +516,11 @@ upper( }), }, }, - cty.DynamicVal, - 1, + cty.ObjectVal(map[string]cty.Value{ + "hello": cty.StringVal("world"), + "goodbye": cty.StringVal("earth"), + }).Mark("marked"), + 0, }, { `{"${var.greeting}" = "world"}`, @@ -918,20 +921,44 @@ upper( }), 0, }, - { // Error when using marked value as object key + { + // Mark object if keys include marked values, members retain + // their original marks in their values `{for v in things: v => "${v}-friend"}`, &hcl.EvalContext{ Variables: map[string]cty.Value{ "things": cty.MapVal(map[string]cty.Value{ - "a": cty.StringVal("rosie").Mark("sensitive"), + "a": cty.StringVal("rosie").Mark("marked"), "b": cty.StringVal("robin"), + // Check for double-marking when a key val has a duplicate mark + "c": cty.StringVal("rowan").Mark("marked"), + "d": cty.StringVal("ruben").Mark("also-marked"), }), }, }, cty.ObjectVal(map[string]cty.Value{ + "rosie": cty.StringVal("rosie-friend").Mark("marked"), "robin": cty.StringVal("robin-friend"), - }), - 1, + "rowan": cty.StringVal("rowan-friend").Mark("marked"), + "ruben": cty.StringVal("ruben-friend").Mark("also-marked"), + }).WithMarks(cty.NewValueMarks("marked", "also-marked")), + 0, + }, + { // object itself is marked, contains marked value + `{for v in things: v => "${v}-friend"}`, + &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "things": cty.MapVal(map[string]cty.Value{ + "a": cty.StringVal("rosie").Mark("marked"), + "b": cty.StringVal("robin"), + }).Mark("marks"), + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "rosie": cty.StringVal("rosie-friend").Mark("marked"), + "robin": cty.StringVal("robin-friend"), + }).WithMarks(cty.NewValueMarks("marked", "marks")), + 0, }, { `[{name: "Steve"}, {name: "Ermintrude"}].*.name`,