Hardcoded environment-specific values — production database URLs, service endpoints, feature flag literals — mean configuration changes require code changes and redeployments, increasing the chance of accidental environment cross-contamination. CWE-15 (External Control of System or Configuration Setting) and OWASP A05 (Security Misconfiguration) both flag this pattern. NIST 800-53 CM-6 requires configuration management to separate configuration from code. A developer testing against a staging endpoint that is hardcoded in the file may accidentally push a commit that hits production services in a different environment.
Low because hardcoded config values are primarily an operational risk — they create deployment fragility and increase the chance of accidental production access from non-production code.
Centralize all environment-specific values behind process.env and validate required variables at startup:
// lib/config.ts
const config = {
apiUrl: process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3001',
dbUrl: process.env.DATABASE_URL,
featureNewDashboard: process.env.FEATURE_NEW_DASHBOARD === 'true',
} as const
if (typeof window === 'undefined' && !config.dbUrl) {
throw new Error('DATABASE_URL is required')
}
export default config
Remove any string literals that embed production hostnames, service URLs, or environment-specific feature toggles from source files. Use .env.example with placeholder values to document all required variables.
ID: security-hardening.secrets-config.config-separation
Severity: low
What to look for: Enumerate every configuration value that differs between environments (dev, staging, production). For each, check whether the application properly separates environment-specific configuration (database URLs, feature flags, service endpoints, log levels) from application code. Look for hardcoded production URLs, environment-specific conditionals with literal values, or feature toggles implemented as code constants instead of environment variables.
Pass criteria: Environment-specific values (endpoints, feature flags, service URLs, log levels) are loaded from environment variables or a configuration service. Code doesn't embed environment-specific string literals. Different environments (dev, staging, prod) use different values without code changes — 100% of environment-specific values must be externalized. Report: "X environment-specific configs found, all Y separated from code."
Fail criteria: Production database URLs, service endpoints, or feature flag values are hardcoded in source files. Different environments require code changes to switch.
Skip (N/A) when: Never — all non-trivial applications have environment-specific configuration.
Detail on fail: "Production API endpoint hardcoded as a string literal in lib/client.ts" or "Feature flags implemented as constants in config.ts with production values committed — requires code change to enable/disable"
Remediation: Externalize all environment-specific values:
// lib/config.ts — centralized config with validation
const config = {
apiUrl: process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3001',
dbUrl: process.env.DATABASE_URL,
logLevel: process.env.LOG_LEVEL ?? 'info',
featureFlags: {
newDashboard: process.env.FEATURE_NEW_DASHBOARD === 'true',
},
} as const
// Validate required server-side vars at startup
if (typeof window === 'undefined') {
if (!config.dbUrl) throw new Error('DATABASE_URL is required')
}
export default config