Summary
TFExpression.resolveExpressionPart() renders tokenized and plain object maps by interpolating object keys directly into double-quoted strings in the generated Terraform expression output — without any escaping. A map key containing a double quote ("), backslash (\), ${...}, or %{...} sequence will either produce syntactically invalid Terraform or silently change the key into a Terraform interpolation/template directive.
Two rendering paths are affected:
- Expression rendering (
packages/cdktn/src/tfExpression.ts lines 54–60 and 69–73) — inside ${ … } expression bodies
- HCL map rendering (
packages/cdktn/src/hcl/render.ts line 22) — for static HCL maps
Affected code
Expression rendering — tfExpression.ts
// Line 54-59: Tokenized (resolvable) object
if (typeof resolvedArg === "object") {
return `{${Object.keys(resolvedArg)
.map(
(key) => `"${key}" = ${this.resolveArg(context, resolvedArg[key])}`, // ❌ key is raw
)
.join(", ")}}`;
}
// Line 69-72: Plain object (same issue)
if (typeof resolvedArg === "object" && resolvedArg !== null) {
return `{${Object.keys(resolvedArg)
.map((key) => `"${key}" = ${this.resolveArg(context, arg[key])}`) // ❌ key is raw
.join(", ")}}`;
}
The existing escapeString() method (lines 108–112) only handles \n and ${:
protected escapeString(str: string) {
return str
.replace(/\n/g, "\\n")
.replace(/\${/g, "$${");
}
…but it is never called on map keys in either object-rendering path.
HCL map rendering — hcl/render.ts
function wrapIdentifierInQuotesIfNeeded(key: string): string {
return /(^\d)|[^A-Za-z0-9_-]/.test(key) ? `"${key}"` : key; // ❌ key is raw
}
The HCL renderer has escapeQuotes() (line 10–16) for values, but it is not applied to map keys.
Reproduction
Any user code that passes an object literal or tokenized map containing a problematic key into a Terraform expression triggers the bug.
import { Fn } from "cdktf";
// Double quote in key — produces broken HCL
Fn.keys({ keywithquotes: "value" });
// Renders to: ${keys({"key"with"quotes" = "value"})} ← ❌ unterminated string
// Backslash in key — can escape the closing quote
Fn.keys({ "key\\broken": "value" });
// Renders to: ${keys({"key\broken" = "value"})} ← ❌ ambiguous escape
// ${...} in key — opens a Terraform interpolation
Fn.keys({ "${var.secret}": "value" });
// Renders to: ${keys({"${var.secret}" = "value"})} ← ❌ injected interpolation
// %{...} in key — opens a Terraform template directive
Fn.keys({ "%{ if true }injected%{ endif }": "value" });
// Renders to: ${keys({"%{ if true }injected%{ endif }" = "value"})} ← ❌ template directive
Suggested fix
- Extend
escapeString() in TFExpression to also escape ", \, %{, and control characters (backslash must be escaped first, before quote escaping introduces its own backslash)
- Call
escapeString() on map keys in both object-rendering paths in resolveExpressionPart()
- Apply the same fix to
wrapIdentifierInQuotesIfNeeded() in hcl/render.ts
Impact
- Corruption: keys with quotes/backslashes produce syntactically invalid HCL, causing
terraform plan/apply to fail at parse time
- Security concern: keys containing
${...} or %{...} could be interpreted as Terraform interpolations or template directives, potentially allowing injection through user-controlled map keys
Flagged during review of PR #296 by @so0k.
Summary
TFExpression.resolveExpressionPart()renders tokenized and plain object maps by interpolating object keys directly into double-quoted strings in the generated Terraform expression output — without any escaping. A map key containing a double quote ("), backslash (\),${...}, or%{...}sequence will either produce syntactically invalid Terraform or silently change the key into a Terraform interpolation/template directive.Two rendering paths are affected:
packages/cdktn/src/tfExpression.tslines 54–60 and 69–73) — inside${ … }expression bodiespackages/cdktn/src/hcl/render.tsline 22) — for static HCL mapsAffected code
Expression rendering —
tfExpression.tsThe existing
escapeString()method (lines 108–112) only handles\nand${:…but it is never called on map keys in either object-rendering path.
HCL map rendering —
hcl/render.tsThe HCL renderer has
escapeQuotes()(line 10–16) for values, but it is not applied to map keys.Reproduction
Any user code that passes an object literal or tokenized map containing a problematic key into a Terraform expression triggers the bug.
Suggested fix
escapeString()inTFExpressionto also escape",\,%{, and control characters (backslash must be escaped first, before quote escaping introduces its own backslash)escapeString()on map keys in both object-rendering paths inresolveExpressionPart()wrapIdentifierInQuotesIfNeeded()inhcl/render.tsImpact
terraform plan/applyto fail at parse time${...}or%{...}could be interpreted as Terraform interpolations or template directives, potentially allowing injection through user-controlled map keysFlagged during review of PR #296 by @so0k.