Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Escape deprecation messages before interpolating into source text #9371

Merged
merged 4 commits into from Apr 8, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG_PENDING.md
Expand Up @@ -14,3 +14,5 @@

### Bug Fixes

- [codegen/node] - Fix an issue with escaping deprecation messages.
[#9371](https://github.com/pulumi/pulumi/pull/9371)
19 changes: 15 additions & 4 deletions pkg/codegen/nodejs/gen.go
Expand Up @@ -64,6 +64,17 @@ func title(s string) string {
return string(append([]rune{unicode.ToUpper(runes[0])}, runes[1:]...))
}

// escape returns the string escaped for a JS string literal
func escape(s string) string {
// Seems the most fool-proof way of doing this is by using the JSON marshaler and then stripping the surrounding quotes
escaped, err := json.Marshal(s)
contract.AssertNoError(err)
contract.Assertf(len(escaped) >= 2, "JSON(%s) expected a quoted string but returned %s", s, escaped)
contract.Assertf(escaped[0] == byte('"') && escaped[len(escaped)-1] == byte('"'), "JSON(%s) expected a quoted string but returned %s", s, escaped)

return string(escaped)[1:(len(escaped) - 1)]
}

// camel converts s to camel case.
//
// Examples:
Expand Down Expand Up @@ -648,7 +659,7 @@ func (mod *modContext) genResource(w io.Writer, r *schema.Resource) error {
fmt.Fprintf(w, " public static get(name: string, id: pulumi.Input<pulumi.ID>, %sopts?: pulumi.%s): %s {\n",
stateParam, optionsType, name)
if r.DeprecationMessage != "" && mod.compatibility != kubernetes20 {
fmt.Fprintf(w, " pulumi.log.warn(\"%s is deprecated: %s\")\n", name, r.DeprecationMessage)
fmt.Fprintf(w, " pulumi.log.warn(\"%s is deprecated: %s\")\n", name, escape(r.DeprecationMessage))
}
fmt.Fprintf(w, " return new %s(name, %s{ ...opts, id: id });\n", name, stateRef)
fmt.Fprintf(w, " }\n")
Expand Down Expand Up @@ -815,7 +826,7 @@ func (mod *modContext) genResource(w io.Writer, r *schema.Resource) error {
argsType, stateType, optionsType)
}
if r.DeprecationMessage != "" && mod.compatibility != kubernetes20 {
fmt.Fprintf(w, " pulumi.log.warn(\"%s is deprecated: %s\")\n", name, r.DeprecationMessage)
fmt.Fprintf(w, " pulumi.log.warn(\"%s is deprecated: %s\")\n", name, escape(r.DeprecationMessage))
}
fmt.Fprintf(w, " let resourceInputs: pulumi.Inputs = {};\n")
fmt.Fprintf(w, " opts = opts || {};\n")
Expand Down Expand Up @@ -954,7 +965,7 @@ func (mod *modContext) genResource(w io.Writer, r *schema.Resource) error {
fmt.Fprintf(w, " %s(%s): %s {\n", methodName, argsig, retty)
if fun.DeprecationMessage != "" {
fmt.Fprintf(w, " pulumi.log.warn(\"%s.%s is deprecated: %s\")\n", name, methodName,
fun.DeprecationMessage)
escape(fun.DeprecationMessage))
}

// Zero initialize the args if empty and necessary.
Expand Down Expand Up @@ -1084,7 +1095,7 @@ func (mod *modContext) genFunction(w io.Writer, fun *schema.Function) error {
fmt.Fprintf(w, "export function %s(%sopts?: pulumi.InvokeOptions): Promise<%s> {\n",
name, argsig, functionReturnType(fun))
if fun.DeprecationMessage != "" && mod.compatibility != kubernetes20 {
fmt.Fprintf(w, " pulumi.log.warn(\"%s is deprecated: %s\")\n", name, fun.DeprecationMessage)
fmt.Fprintf(w, " pulumi.log.warn(\"%s is deprecated: %s\")\n", name, escape(fun.DeprecationMessage))
}

// Zero initialize the args if empty and necessary.
Expand Down
24 changes: 24 additions & 0 deletions pkg/codegen/nodejs/gen_test.go
Expand Up @@ -218,3 +218,27 @@ func Test_isStringType(t *testing.T) {
})
}
}

func TestEscape(t *testing.T) {
t.Parallel()
tests := []struct {
input string
expected string
}{
{"test", "test"},
{"sub\"string\"", "sub\\\"string\\\""},
{"slash\\s", "slash\\\\s"},
{"N\\A \"bad data\"", "N\\\\A \\\"bad data\\\""},
}
for _, tt := range tests {
tt := tt
t.Run(tt.input, func(t *testing.T) {
t.Parallel()

got := escape(tt.input)
if tt.expected != got {
t.Errorf("escape(%s) was %s want %s", tt.input, got, tt.expected)
}
})
}
}