Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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.
7 changes: 5 additions & 2 deletions packages/compiler/generated-defs/TypeSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -434,15 +434,18 @@ 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
* @secret
* 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.
Expand Down
4 changes: 2 additions & 2 deletions packages/compiler/lib/std/decorators.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -271,15 +271,15 @@ 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
* @secret
* 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.
Expand Down
10 changes: 3 additions & 7 deletions packages/compiler/src/lib/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};

Expand Down
120 changes: 98 additions & 22 deletions packages/compiler/test/decorators/decorators.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1135,53 +1135,129 @@ 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
scalar A extends int32;
`,
);

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 {
@secret
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")!));
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading