Why AI tools produce this: AI coding tools generate this anti-pattern by default, it appears in a large share of AI-written auth code.
Why this matters
The client secret authenticates your application to the authorization server; committed to source control it is one grep/git-history search away from compromise, letting an attacker impersonate your client, mint tokens, and exchange authorization codes (CWE-798). AI-generated snippets inline the secret to make the sample "just work" and it ships unchanged.
Load the secret at runtime from the environment or a secret manager: let secret = std::env::var("OAUTH_CLIENT_SECRET")?; let client = BasicClient::new(client_id) .set_client_secret(ClientSecret::new(secret)); Never commit client secrets to source control.
VULNERABLE
vulnerable.rs
use oauth2::basic::BasicClient;use oauth2::{ClientId, ClientSecret};// Client secret hardcoded as a string literal — one grep away from compromise.fn build_client_to_string() -> BasicClient { // ruleid: auth.rust.oauth.hardcoded-client-secret BasicClient::new(ClientId::new("my_client_id".to_string())) .set_client_secret(ClientSecret::new("s3cr3t-abcdef-123456".to_string()))}// Hardcoded via .to_owned().fn build_client_to_owned() { // ruleid: auth.rust.oauth.hardcoded-client-secret let _ = ClientSecret::new("s3cr3t-abcdef-123456".to_owned());}// Hardcoded via String::from.fn build_client_string_from() { // ruleid: auth.rust.oauth.hardcoded-client-secret let _ = ClientSecret::new(String::from("s3cr3t-abcdef-123456"));}// Hardcoded via .into().fn build_client_into() { // ruleid: auth.rust.oauth.hardcoded-client-secret let _ = ClientSecret::new("s3cr3t-abcdef-123456".into());}fn main() {}
SAFE
safe.rs
use oauth2::basic::BasicClient;use oauth2::{ClientId, ClientSecret};// Safe: secret loaded from the environment at runtime.fn build_client_from_env() -> Result<(), Box<dyn std::error::Error>> { let secret = std::env::var("OAUTH_CLIENT_SECRET")?; // ok: auth.rust.oauth.hardcoded-client-secret let _ = ClientSecret::new(secret); Ok(())}// Safe: env var read inline and converted, still not a literal.fn build_client_inline_env() -> Result<(), Box<dyn std::error::Error>> { // ok: auth.rust.oauth.hardcoded-client-secret let _ = ClientSecret::new(std::env::var("OAUTH_CLIENT_SECRET")?); Ok(())}// Safe: derived from a variable, not a string literal.fn build_client_from_var(secret: String) { // ok: auth.rust.oauth.hardcoded-client-secret let _ = ClientSecret::new(secret);}fn main() {}
Suppressing this rule
If a finding is a genuine false positive, scope the suppression to the exact line and leave a reason, never disable the rule project-wide. Disable directives are line-scoped by design.