From 1eb95b6b8d4e1f093816972a5d381742a338a1e5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Sep 2025 18:41:37 +0000 Subject: [PATCH 1/3] Initial plan From cd74edc6c2214b259fe51a44bbbf670a4fe3eefa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Sep 2025 19:05:37 +0000 Subject: [PATCH 2/3] Widen @secret decorator to accept Model, Union, and Enum types Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com> --- packages/compiler/generated-defs/TypeSpec.ts | 4 +- packages/compiler/lib/std/decorators.tsp | 4 +- packages/compiler/src/lib/decorators.ts | 10 +- .../test/decorators/decorators.test.ts | 120 ++++++++++++++---- 4 files changed, 105 insertions(+), 33 deletions(-) diff --git a/packages/compiler/generated-defs/TypeSpec.ts b/packages/compiler/generated-defs/TypeSpec.ts index 1c579f07457..12f2a7c431a 100644 --- a/packages/compiler/generated-defs/TypeSpec.ts +++ b/packages/compiler/generated-defs/TypeSpec.ts @@ -434,7 +434,7 @@ export type MaxValueExclusiveDecorator = ( ) => void; /** - * Mark this string as a secret value that should be treated carefully to avoid exposure + * Mark this value as a secret value that should be treated carefully to avoid exposure * * @example * ```typespec @@ -442,7 +442,7 @@ export type MaxValueExclusiveDecorator = ( * scalar Password is string; * ``` */ -export type SecretDecorator = (context: DecoratorContext, target: Scalar | ModelProperty) => void; +export type SecretDecorator = (context: DecoratorContext, target: Scalar | ModelProperty | Model | Union | Enum) => void; /** * Attaches a tag to an operation, interface, or namespace. Multiple `@tag` decorators can be specified to attach multiple tags to a TypeSpec element. diff --git a/packages/compiler/lib/std/decorators.tsp b/packages/compiler/lib/std/decorators.tsp index daafa0d6d9b..90f778ba20c 100644 --- a/packages/compiler/lib/std/decorators.tsp +++ b/packages/compiler/lib/std/decorators.tsp @@ -271,7 +271,7 @@ extern dec minValueExclusive(target: numeric | ModelProperty, value: valueof num extern dec maxValueExclusive(target: numeric | ModelProperty, value: valueof numeric); /** - * Mark this string as a secret value that should be treated carefully to avoid exposure + * Mark this value as a secret value that should be treated carefully to avoid exposure * * @example * ```typespec @@ -279,7 +279,7 @@ extern dec maxValueExclusive(target: numeric | ModelProperty, value: valueof num * scalar Password is string; * ``` */ -extern dec secret(target: string | ModelProperty); +extern dec secret(target: Scalar | ModelProperty | Model | Union | Enum); /** * Attaches a tag to an operation, interface, or namespace. Multiple `@tag` decorators can be specified to attach multiple tags to a TypeSpec element. diff --git a/packages/compiler/src/lib/decorators.ts b/packages/compiler/src/lib/decorators.ts index 335e8899be8..e28437abd89 100644 --- a/packages/compiler/src/lib/decorators.ts +++ b/packages/compiler/src/lib/decorators.ts @@ -750,19 +750,15 @@ export const $maxValueExclusive: MaxValueExclusiveDecorator = ( const [isSecret, markSecret] = useStateSet(createStateSymbol("secretTypes")); /** - * Mark a string as a secret value that should be treated carefully to avoid exposure + * Mark a value as a secret value that should be treated carefully to avoid exposure * @param context Decorator context - * @param target Decorator target, either a string model or a property with type string. + * @param target Decorator target: a scalar, model property, model, union, or enum. */ export const $secret: SecretDecorator = ( context: DecoratorContext, - target: Scalar | ModelProperty, + target: Scalar | ModelProperty | Model | Union | Enum, ) => { validateDecoratorUniqueOnNode(context, target, $secret); - - if (!validateTargetingAString(context, target, "@secret")) { - return; - } markSecret(context.program, target); }; diff --git a/packages/compiler/test/decorators/decorators.test.ts b/packages/compiler/test/decorators/decorators.test.ts index 0723b1f4b80..648283e2901 100644 --- a/packages/compiler/test/decorators/decorators.test.ts +++ b/packages/compiler/test/decorators/decorators.test.ts @@ -1135,24 +1135,20 @@ describe("compiler: built-in decorators", () => { ok(isSecret(runner.program, A.properties.get("a")!)); }); - it("emit diagnostic if model is not a string", async () => { - const diagnostics = await runner.diagnose( + it("can be applied on a model", async () => { + const { A } = (await runner.compile( ` @test @secret model A {} `, - ); + )) as { A: Model }; - expectDiagnostics(diagnostics, { - code: "decorator-wrong-target", - message: - "Cannot apply @secret decorator to A since it is not assignable to string | ModelProperty", - }); + ok(isSecret(runner.program, A)); }); - it("emit diagnostic if model is a different intrinsic type(not a string)", async () => { - const diagnostics = await runner.diagnose( + it("can be applied on a non-string scalar", async () => { + const { A } = await runner.compile( ` @test @secret @@ -1160,15 +1156,11 @@ describe("compiler: built-in decorators", () => { `, ); - expectDiagnostics(diagnostics, { - code: "decorator-wrong-target", - message: - "Cannot apply @secret decorator to A since it is not assignable to string | ModelProperty", - }); + ok(isSecret(runner.program, A)); }); - it("emit diagnostic if model property is not a string type", async () => { - const diagnostics = await runner.diagnose( + it("can be applied on a model property with non-string type", async () => { + const { A } = (await runner.compile( ` @test model A { @@ -1176,12 +1168,96 @@ describe("compiler: built-in decorators", () => { a: int32; } `, - ); + )) as { A: Model }; - expectDiagnostics(diagnostics, { - code: "decorator-wrong-target", - message: "Cannot apply @secret decorator to type it is not a string", - }); + ok(isSecret(runner.program, A.properties.get("a")!)); + }); + + it("can be applied on a union", async () => { + const { A } = (await runner.compile( + ` + @test + @secret + union A { + x: string, + y: int32 + } + `, + )) as { A: Union }; + + ok(isSecret(runner.program, A)); + }); + + it("can be applied on an enum", async () => { + const { A } = (await runner.compile( + ` + @test + @secret + enum A { + One: "one", + Two: "two" + } + `, + )) as { A: Enum }; + + ok(isSecret(runner.program, A)); + }); + + it("can be applied on a model property with model type", async () => { + const { A } = (await runner.compile( + ` + @secret + model SecretModel { + data: string; + } + + @test + model A { + @secret + secret: SecretModel; + } + `, + )) as { A: Model }; + + ok(isSecret(runner.program, A.properties.get("secret")!)); + }); + + it("can be applied on a model property with union type", async () => { + const { A } = (await runner.compile( + ` + union SecretUnion { + x: string, + y: int32 + } + + @test + model A { + @secret + secret: SecretUnion; + } + `, + )) as { A: Model }; + + ok(isSecret(runner.program, A.properties.get("secret")!)); + }); + + it("can be applied on a model property with enum type", async () => { + const { A } = (await runner.compile( + ` + enum SecretEnum { + One: "one", + Two: "two" + } + + @test + model A { + @secret + secret: SecretEnum; + } + `, + )) as { A: Model }; + + ok(isSecret(runner.program, A.properties.get("secret")!)); }); }); From e1c6f4da5c985c18e0d0af2b9980b7ca4110cd50 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:30:06 +0000 Subject: [PATCH 3/3] Include build-generated changes and add changelog entry for @secret decorator widening Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com> --- .../secret-decorator-widen-targets-2025-9-24-20-28-0.md | 7 +++++++ packages/compiler/generated-defs/TypeSpec.ts | 5 ++++- .../docs/docs/standard-library/built-in-decorators.md | 4 ++-- 3 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 .chronus/changes/secret-decorator-widen-targets-2025-9-24-20-28-0.md diff --git a/.chronus/changes/secret-decorator-widen-targets-2025-9-24-20-28-0.md b/.chronus/changes/secret-decorator-widen-targets-2025-9-24-20-28-0.md new file mode 100644 index 00000000000..c3b56e46071 --- /dev/null +++ b/.chronus/changes/secret-decorator-widen-targets-2025-9-24-20-28-0.md @@ -0,0 +1,7 @@ +--- +changeKind: feature +packages: + - "@typespec/compiler" +--- + +Widen target types for the `@secret` decorator to include Model, Union, and Enum types, in addition to existing Scalar and ModelProperty targets. This allows marking any data type as secret for comprehensive data sensitivity handling. \ No newline at end of file diff --git a/packages/compiler/generated-defs/TypeSpec.ts b/packages/compiler/generated-defs/TypeSpec.ts index 12f2a7c431a..297c90b94ae 100644 --- a/packages/compiler/generated-defs/TypeSpec.ts +++ b/packages/compiler/generated-defs/TypeSpec.ts @@ -442,7 +442,10 @@ export type MaxValueExclusiveDecorator = ( * scalar Password is string; * ``` */ -export type SecretDecorator = (context: DecoratorContext, target: Scalar | ModelProperty | Model | Union | Enum) => void; +export type SecretDecorator = ( + context: DecoratorContext, + target: Scalar | ModelProperty | Model | Union | Enum, +) => void; /** * Attaches a tag to an operation, interface, or namespace. Multiple `@tag` decorators can be specified to attach multiple tags to a TypeSpec element. diff --git a/website/src/content/docs/docs/standard-library/built-in-decorators.md b/website/src/content/docs/docs/standard-library/built-in-decorators.md index cdef4e9f1b0..2795cb7a6ba 100644 --- a/website/src/content/docs/docs/standard-library/built-in-decorators.md +++ b/website/src/content/docs/docs/standard-library/built-in-decorators.md @@ -1136,14 +1136,14 @@ It is invalid to call this decorator with no visibility modifiers. ### `@secret` {#@secret} -Mark this string as a secret value that should be treated carefully to avoid exposure +Mark this value as a secret value that should be treated carefully to avoid exposure ```typespec @secret ``` #### Target -`string | ModelProperty` +`Scalar | ModelProperty | Model | Union | Enum` #### Parameters None