SC005: NoHardcodedSecretsV2

Overview

Property Value
ID SC005
Name NoHardcodedSecretsV2
Group security
Severity ERROR

Description

Enhanced AST-based secret detection that analyzes Python source code structure to identify hardcoded secrets with higher accuracy and fewer false positives than regex-based scanning.

This advanced check uses Abstract Syntax Tree (AST) analysis to:

  • Understand code context (assignments, function calls, class attributes)
  • Detect secrets in string literals, f-strings, and concatenations
  • Identify secrets passed to functions, APIs, and configurations
  • Reduce false positives by analyzing variable names and usage patterns

What it checks

AST-based detection patterns

The check analyzes the code structure for:

Variable assignments

# Detected: assignment to secret-named variable
api_key = "sk-1234567890abcdef"
AWS_SECRET_KEY = "wJalrXUtnFEMI/K7MDENG"
database_password = "super_secret_123"

Dictionary literals

# Detected: secrets in config dictionaries
config = {
    "api_key": "sk-1234567890abcdef",
    "password": "hardcoded_password",
}

Function call arguments

# Detected: secrets passed to functions
client = OpenAI(api_key="sk-1234567890abcdef")
connect(password="database_password")

Class attributes

# Detected: secrets in class definitions
class Config:
    SECRET_KEY = "my-secret-key-12345"
    API_TOKEN = "ghp_xxxxxxxxxxxx"

F-strings and concatenation

# Detected: secrets constructed via f-strings
auth_header = f"Bearer sk-{secret_part}"
full_key = "AKIA" + "1234567890ABCDEF"

Secret patterns detected

Pattern Description
sk-... OpenAI API keys
AKIA... AWS Access Key IDs
ghp_*, gho_*, ghs_* GitHub tokens
xox[baprs]-* Slack tokens
-----BEGIN * PRIVATE KEY----- Private keys
High-entropy strings Generic secrets (configurable)

Result states

  • PASSED: No hardcoded secrets found
  • FAILED: Secrets detected with file, line, and context
  • SKIPPED: Unable to parse Python files

How to fix

Use environment variables

import os

# Bad: hardcoded
API_KEY = "sk-1234567890abcdef"

# Good: from environment
API_KEY = os.environ["API_KEY"]
# or with default
API_KEY = os.environ.get("API_KEY", "")

Use python-dotenv

from dotenv import load_dotenv
import os

load_dotenv()  # Load from .env file

API_KEY = os.environ.get("API_KEY")
DATABASE_URL = os.environ.get("DATABASE_URL")

Create a .env file (never commit to git):

# .env
API_KEY=sk-1234567890abcdef
DATABASE_URL=postgresql://user:pass@localhost/db

Use a secrets manager

# AWS Secrets Manager
import boto3

client = boto3.client('secretsmanager')
secret = client.get_secret_value(SecretId='my-secret')

# HashiCorp Vault
import hvac

client = hvac.Client(url='https://vault.example.com')
secret = client.secrets.kv.read_secret_version(path='my-secret')

Use pydantic-settings

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    api_key: str
    database_url: str

    class Config:
        env_file = ".env"

settings = Settings()  # Loads from environment/.env

Protect your .gitignore

# Secrets and credentials
.env
.env.*
*.pem
*.key
secrets.toml
credentials.json

Comparison with SC001

Feature SC001 (NoHardcodedSecrets) SC005 (NoHardcodedSecretsV2)
Detection method Regex patterns AST analysis
Context awareness Limited Full code structure
False positive rate Higher Lower
f-string detection Basic Full support
Dictionary detection Limited Full support
Performance Faster Slightly slower

Why ERROR severity?

This check is an ERROR because:

  • Hardcoded secrets are critical security vulnerabilities
  • Once committed, secrets persist in git history
  • AST analysis provides high-confidence detections
  • False positives are minimized, so findings should be addressed

Configuration

Skip this check

[tool.pycmdcheck]
skip = ["SC005"]

CLI

pycmdcheck --skip SC005

Skip entire security group

pycmdcheck --skip-group security