Recommend SafeDebug over Debug for Rust (#8282)

SafeDebug still needs to be added, but that shouldn't take long if these guidelines seem appropriate.
This commit is contained in:
Heath Stewart 2024-11-19 11:00:32 -08:00 коммит произвёл GitHub
Родитель 0f6868ef17
Коммит 951f584f85
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
3 изменённых файлов: 46 добавлений и 11 удалений

Просмотреть файл

@ -6,6 +6,32 @@ folder: rust
sidebar: general_sidebar
---
## Safety {#rust-safety}
The following guidelines are to foster secure code not only within Azure SDK for Rust, but on behalf of our customers.
### Debug Trait {#rust-safety-debug}
{% include requirement/MAY id="rust-safety-debug-derive" %} derive or implement `Debug` on types as long as you guarantee no PII may be leaked.
To elide some fields from `Debug` output, you may use `finish_non_exhaustive()` like so:
```rust
use std::fmt;
impl fmt::Debug for MyModel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MyModel")
.field("id", &self.id)
.finish_non_exhaustive()
}
}
```
{% include requirement/SHOULD id="rust-safety-debug-safedebug" %} derive or implement `azure_core::fmt::SafeDebug` on types if you need a `Debug` implementation but cannot reasonably guarantee no PII may be leaked.
`SafeDebug` will only output the name of the type or, if information is available in TypeSpec, show only fields that have been declared safe from leaking PII.
## Service Clients {#rust-client}
Implementation details of [service clients](introduction.md#rust-client).

Просмотреть файл

@ -168,16 +168,16 @@ Client options should be plain old data structures to allow easy, idiomatic crea
{% include requirement/MUST id="rust-client-configuration-clone" %} derive `Clone` to support cloning client configuration for other clients.
{% include requirement/MUST id="rust-client-configuration-debug" %} derive `Debug` to support printing members for diagnostics purposes.
{% include requirement/SHOULDNOT id="rust-client-configuration-debug" %} derive `Debug` since this may inadvertently leak PII. Derive [`azure_core::fmt::SafeDebug`][rust-safety-debug] instead.
{% include requirement/MUST id="rust-client-configuration-default" %} implement `Default` to support creating default client configuration including the default `api_version` used when calling into the service.
The requirements above would define an example client options struct like:
```rust
use azure_core::ClientOptions;
use azure_core::{ClientOptions, fmt::SafeDebug};
#[derive(Clone, Debug)]
#[derive(Clone, SafeDebug)]
pub struct SecretClientOptions {
pub api_version: String,
pub client_options: ClientOptions,
@ -220,7 +220,7 @@ impl Default for SecretClientOptions {
{% include requirement/MUST id="rust-client-methods-params" %} use the service specified name of all parameters.
{% include requirement/MUST id="rust-client-methods-configuration-name" %} define a client method options struct with the same as the client, client method name, and "Options" e.g., a `set_secret` takes an `Option<SecretClientSetSecretOptions>` as the last parameter.
{% include requirement/MUST id="rust-client-methods-configuration-name" %} define a client method options struct with the same name as the client, client method name, and "Options" e.g., a `set_secret` takes an `Option<SecretClientSetSecretOptions>` as the last parameter.
This is required even if the service method does not currently take any options because - should it ever add options - the client method signature does not have to change and will not break callers.
{% include requirement/SHOULD id="rust-client-methods-configuration-namespace" %} export client method option structs from the root module of the client library e.g., `azure_security_keyvault_secrets`.
@ -231,14 +231,14 @@ This is required even if the service method does not currently take any options
{% include requirement/MUST id="rust-client-methods-configuration-clone" %} derive `Clone` to support cloning method configuration for additional client method invocations.
{% include requirement/MUST id="rust-client-methods-configuration-debug" %} derive `Debug` to support printing members for diagnostics purposes.
{% include requirement/MUST id="rust-client-methods-configuration-debug" %} derive `Debug` since this may inadvertently leak PII. Derive [`azure_core::fmt::SafeDebug`][rust-safety-debug] instead.
{% include requirement/MUST id="rust-client-methods-configuration-default" %} derive or implement `Default` to support creating default method configuration.
The requirements above would define an example client options struct like:
```rust
use azure_core::ClientMethodOptions;
use azure_core::{ClientMethodOptions, fmt::SafeDebug};
impl SecretClientMethods for SecretClient {
async fn set_secret(
@ -251,7 +251,7 @@ impl SecretClientMethods for SecretClient {
}
}
#[derive(Clone, Debug, Default)]
#[derive(Clone, SafeDebug, Default)]
pub struct SecretClientSetSecretOptions {
pub enabled: Option<bool>,
pub method_options: ClientMethodOptions,
@ -421,7 +421,9 @@ The `azure_core` crate exposes an abstract type called `azure_core::Poller<T>`,
{% include requirement/MUST id="rust-etag-options" %} define ETag-related options e.g., `if_match`, `if_none_match`, etc., in the service method options e.g.:
```rust
#[derive(Clone, Debug)]
use azure_core::SafeDebug;
#[derive(Clone, SafeDebug)]
pub struct SetSecretOptions {
enabled: Option<bool>,
if_match: Option<azure_core::ETag>,
@ -527,7 +529,9 @@ If you do implement a builder, it must be defined according to the following gui
{% include requirement/MUST id="rust-enums-names" %} implement all enumeration variations as PascalCase.
{% include requirement/MUST id="rust-enums-derive" %} derive or implement `Clone`, `Debug`, `Eq`, and `PartialEq` for all enums.
{% include requirement/MUST id="rust-enums-derive" %} derive or implement `Clone`, `Eq`, and `PartialEq` for all enums.
{% include requirement/SHOULDNOT id="rust-enums-debug" %} derive `Debug` since this may inadvertently leak PII. Derive [`azure_core::fmt::SafeDebug`][rust-safety-debug] instead.
{% include requirement/MUST id="rust-enums-derive-copy" %} derive `Copy` for all fixed enums.
@ -557,7 +561,9 @@ See [RFC 2008][rust-lang-rfc-2008] for more information.
{% include requirement/MUST id="rust-enum-fixed" %} implement all fixed enumerations using only defined variants:
```rust
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
use azure_core::SafeDebug;
#[derive(Clone, Copy, SafeDebug, Eq, PartialEq, Deserialize, Serialize)]
#[non_exhaustive]
pub enum FixedEnum {
#[serde(rename = "foo")]
@ -570,7 +576,9 @@ pub enum FixedEnum {
{% include requirement/MUST id="rust-enum-extensible" %} implement all extensible enumerations - those which may take a variant that is not defined - using defined variants and an untagged `UnknownValue`:
```rust
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
use azure_core::SafeDebug;
#[derive(Clone, SafeDebug, Eq, PartialEq, Deserialize, Serialize)]
#[non_exhaustive]
pub enum ExtensibleEnum {
#[serde(rename = "foo")]

Просмотреть файл

@ -24,3 +24,4 @@
[general-recorded-tests]: {{ site.baseurl }}{% link docs/general/implementation.md %}#recorded-tests
[registered package list]: {{ site.baseurl }}{% link docs/tables/registered_namespaces.md %}
[rust-client-convenience]: {{ site.baseurl }}{% link docs/rust/implementation.md %}#rust-client-convenience
[rust-safety-debug]: {{ site.baseurl }}{% link docs/rust/implementation.md %}#rust-safety-debug