agentsclimarketplace

51 env config hardening

Skill FluxonLab/Skillry/plugins/security/skills/51-env-config-hardening

Installable, permission-bounded, multi-platform agent skills & subagents for Claude Code, Codex, Copilot & Gemini/Antigravity — 125 skills + 73 subagents across 18 departments, with a validation harness, native plugin marketplace, and full upstream attribution. by FluxonLab.

Install
npx -y skills add FluxonLab/Skillry --skill 51-env-config-hardening

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when you need to harden environment handling, config defaults, .env safety, secret loading, and redaction.

SKILL.md

14.3 KB, ~3.5k tokens by cl100k_base, as published. Nobody here has run it

Env Config Hardening

Purpose

Scope note: For committed-secret scanning and credential leak detection (git history, CORS, JWT, injection sinks), use security-and-secrets-review (47); this skill focuses on env var validation, config defaults, and 12-factor hardening.

Audit how the project loads, validates, and protects environment variables and secrets. Catch .env files committed to git, default secrets in code, missing schema validation, missing redaction in logs, and deviations from 12-factor config principles. Produce concrete fixes with file references — not just observations.

The practical goal: the app must fail fast with a clear, actionable error when a required config value is missing or malformed, and it must never silently start with a dangerous default. Every finding includes the file and line where the risk lives and the smallest safe fix.

When to use

  • A .env file or hardcoded secret was found (or suspected) in the codebase or git history.
  • The project has no config validation layer and starts silently with missing or malformed env vars.
  • NODE_ENV is not set or is set inconsistently across environments.
  • Secret rotation is being planned and you need to confirm the codebase supports it cleanly.
  • A new service is being added and needs to follow existing config conventions.
  • A Docker image or CI pipeline change touches how secrets are injected into the container.

When not to use

  • The project uses a secrets manager (Vault, AWS Secrets Manager, GCP Secret Manager) exclusively and the task is to configure that manager, not the application code.
  • Config hardening was done recently and the task is something else entirely.
  • The codebase is a throwaway script with no production deployment.

Procedure

  1. Check .gitignore for .env files. Confirm .env, .env.local, .env.*.local, and any environment-specific variants (*.env, .env.production, .env.staging) are excluded. Run git ls-files | grep -E '\.env' to surface any currently tracked env files, and git log --all --full-history -- '*.env' '**/.env' to detect past commits.

  2. Scan for hardcoded secrets. Search for assignment patterns: password =, secret =, api_key =, token =, private_key, base64-encoded strings longer than 40 characters, and PEM headers (-----BEGIN). Look in config files, seed files, test fixtures, migration scripts, and Dockerfiles. Every hit must be reported as file:line — raw values must not appear in the output.

  3. Verify .env.example / .env.template exists and is complete. The template must list every required variable with a placeholder value (not the real value), a comment describing its purpose, and an expected format. Example: DATABASE_URL=postgres://user:pass@host:5432/db # Required. Postgres connection string.. Compare the template against the actual variables consumed by the app.

  4. Inspect config validation. Check for a startup validation step using zod, joi, envalid, t3-env, convict, or equivalent. The app must fail fast with a clear error message if a required variable is missing or has the wrong type — not crash with Cannot read properties of undefined at first usage, and not start silently with a broken config.

  5. Check NODE_ENV handling. Confirm NODE_ENV is set explicitly in all deployment environments. Code that branches on NODE_ENV === 'production' for security features (HTTPS-only cookies, strict CSP, forced HTTPS) must default to the secure branch when NODE_ENV is unset — not to the permissive development branch.

  6. Audit default values. Look for patterns like process.env.SECRET || 'changeme' or process.env.JWT_SECRET || 'dev-secret'. Required secrets must have no fallback default. A required value that silently defaults to a known string in production is a critical misconfiguration.

  7. Verify secret redaction in logs. Search for logging statements that include req.headers, req.body, process.env, or config objects without a redaction step. Confirm the logging middleware strips authorization, cookie, password, secret, token, and x-api-key field names. Check error tracking integrations (Sentry, Datadog) for similar leakage.

  8. Check secret rotation readiness. Secrets should be loaded at request time or at startup through a reload-capable mechanism, not cached in module-level constants in a way that prevents hot rotation. A rotating secret that requires a full process restart to take effect creates a downtime window during rotation.

  9. Inspect Dockerfile and CI config. Confirm ARG vs ENV usage in Dockerfiles — ARG values are visible in image layer history via docker history; secrets must not be passed as ARG. In CI, secrets must be injected via the CI platform's secret store (GitHub Actions secrets.*, GitLab CI variables, CircleCI contexts), never stored in plain text in workflow YAML files.

  10. Verify environment parity. Compare the variable list in .env.example against what each deployment environment (staging, production) actually provides. Flag variables present in the example but absent from deployment configs (silently unset at runtime) and variables present in deployment but absent from the example (undocumented dependency).

Concrete checks

  • .gitignore includes .env, .env.local, and all .env.*.local variants; wildcards like *.env and *.env.* are preferred over per-file entries.
  • git ls-files | grep -E '\.env' returns empty; no env files are currently tracked.
  • git log --all --full-history -- '*.env' returns empty; no env files exist in history.
  • No hardcoded passwords, API keys, or tokens appear in source code, test fixtures, seed files, or migration scripts.
  • .env.example exists and lists every required variable with a placeholder value and a comment.
  • Startup config validation fails fast on missing or invalid variables (zod/joi/envalid parse runs at import time or in the server init function, before any request handler is registered).
  • No required secret has a fallback default value in code.
  • NODE_ENV defaults to the secure branch when unset; the insecure (dev) branch is opt-in, not opt-out.
  • Logging middleware redacts authorization, cookie, password, secret, token, x-api-key fields before writing to any log sink.
  • No secrets passed as Docker ARG; secrets enter containers via environment injection only (--env-file or orchestrator-managed secrets).
  • CI pipeline reads secrets from the platform secret store; no plain-text secret values appear in workflow YAML files.
  • All environment-specific configs (staging, prod) reconciled against .env.example; no variable is silently unset.

Commands

# Check for tracked .env files (must return empty)
git ls-files | grep -E '(^|/)\.env($|\.)' || echo "clean"

# Check .gitignore coverage
git check-ignore -v .env .env.local .env.production 2>/dev/null || echo "WARNING: one or more .env files not gitignored"

# Search git history for .env file commits
git log --all --full-history --oneline -- '*.env' '**/.env' '.env*'

# Hardcoded secret patterns (generic assignment)
rg -niP '(password|passwd|secret|api[_-]?key|token|private[_-]?key)\s*[:=]\s*["\x27][^"\x27]{6,}' \
  -g '!*.lock' -g '!node_modules' -g '!*.min.*' .

# Base64 blob and PEM header detection
rg -nP '-----BEGIN [A-Z ]*PRIVATE KEY-----|-----BEGIN CERTIFICATE-----' -g '!node_modules' .
rg -nP '[A-Za-z0-9+/]{40,}={0,2}' -g '!*.lock' -g '!node_modules' src/ | grep -v "# base64" | head -20

# Fallback default secrets (critical misconfiguration)
rg -n '\|\|\s*["\x27](dev|secret|changeme|password|test|local|default|123)' src/ --type ts --type js

# NODE_ENV security gate (should default to secure, not dev)
rg -n "NODE_ENV\s*!==?\s*['\"]production['\"]" src/ --type ts --type js

# Config validation library present?
rg -n "from 'zod'\|from 'joi'\|from 'envalid'\|from 't3-env'\|from 'convict'" src/ --type ts --type js || \
  echo "No schema validation library detected — startup validation may be missing"

# Logging: check for unredacted sensitive fields
rg -n "console\.log\|logger\.(info|debug|warn|error)" src/ --type ts --type js | \
  grep -E "req\.(headers|body)|process\.env|config\." | head -20

# Dockerfile: ARG used for secrets?
rg -n "^ARG\s+(SECRET|KEY|TOKEN|PASSWORD|PASS)" Dockerfile* docker-compose*.yml 2>/dev/null

# CI: plaintext secret values in workflow files?
rg -n "(password|secret|token|key)\s*:" .github/workflows/ .gitlab-ci.yml 2>/dev/null | \
  grep -v '\$\{\{' | grep -v "secrets\." | grep -v "env\." | head -20

# .env.example vs actual env consumption (find all process.env usages)
rg -n 'process\.env\.\w+' src/ --type ts --type js -o | grep -oP 'process\.env\.\K\w+' | sort -u > /tmp/env_used.txt
grep -oP '^\K[A-Z_]+(?==)' .env.example 2>/dev/null | sort -u > /tmp/env_documented.txt
comm -23 /tmp/env_used.txt /tmp/env_documented.txt && echo "Above vars used but not in .env.example" || true
# Secret rotation readiness: detect module-level const caching of secrets
rg -n "^const\s+\w*(secret|key|token|password)\w*\s*=\s*process\.env" src/ --type ts --type js -i

# Check for dotenv loaded outside dev/test
rg -n "require\('dotenv'\)|import.*dotenv" src/ --type ts --type js | grep -v "test\|spec\|__tests__\|\.test\." | head -10

# Docker: verify ENV vs ARG for secrets (ENV bakes value into image layers too — use runtime injection)
grep -nE "^(ENV|ARG)\s" Dockerfile* 2>/dev/null | head -20

# Error tracking leakage: Sentry/Datadog breadcrumbs with sensitive data
rg -n "Sentry\.(captureException|captureMessage|addBreadcrumb)" src/ --type ts --type js | \
  grep -E "req\.(body|headers)|password|token" | head -10

Severity rubric

SeverityExample
Critical.env file tracked in git (currently or in history). Hardcoded live secret in source code. Required secret has a known string as its default (`
HighNo startup config validation — app starts silently with a broken or missing config. NODE_ENV security gate defaults to the insecure branch. Logging middleware emits req.headers or req.body without redaction.
Medium.env.example missing or incomplete (undocumented required variable). Secret cached at module load time preventing rotation without restart. dotenv loaded unconditionally in production.
LowA variable in .env.example has a real-looking placeholder value instead of a clearly fake one. Staging config has an extra variable not listed in the example (undocumented optional). Verbose startup log includes config object key names.

Common issues & anti-patterns

  • .env in git history: even after deletion from the working tree, the file is recoverable from every clone. Requires git filter-repo or BFG Repo Cleaner to purge from history — and secret rotation is mandatory regardless, because history rewrite does not invalidate the value on existing clones.
  • dotenv loaded unconditionally in production: require('dotenv').config() at the top of server.ts causes the production app to read from a .env file if one exists, silently overriding injected environment variables. dotenv should load only in development and test (if (process.env.NODE_ENV !== 'production')).
  • Config spread across files without a central validation point: secrets in config/database.js, config/redis.js, and src/auth/jwt.js with no single validated config object makes it impossible to audit which variables are required and which have safe defaults.
  • console.log(config) at startup: added for debugging and left in, this logs the entire config object — including all secret values — to stdout in production.
  • NODE_ENV used as a security gate without a default: if (process.env.NODE_ENV !== 'production') { skipAuth() } — when NODE_ENV is unset or empty, auth is skipped. The safe pattern is if (process.env.NODE_ENV === 'development') { skipAuth() } so missing env defaults to secure.
  • Secrets in URL query strings: ?api_key=xxx is logged by every proxy, load balancer, CDN, and browser history. Secrets must travel in request headers or request bodies over TLS.
  • Rotation breaks the app: a secret cached as const jwtSecret = process.env.JWT_SECRET at module import time means deploying a rotated secret requires a full process restart. For high-rotation-frequency secrets, load at request time or use a reload signal handler.
  • Docker ARG for secrets: docker build --build-arg DB_PASSWORD=xxx stores the value in the image's layer history, visible via docker history --no-trunc. Use runtime environment injection (docker run -e or orchestrator secrets mounts) instead.

Required output

## Env Config Hardening Report

### Critical findings
- [FILE:LINE or GIT-SHA] Description, impact, fix command or code change.

### High findings
- ...

### Medium findings
- ...

### Config validation status
- Library used: zod / joi / envalid / t3-env / none
- Validated at startup: yes/no
- Variables validated: list

### .env.example completeness
- Variables in example: N
- Variables consumed by app but missing from example: list
- Variables in example with real-looking values (risk): list

### Redaction status
- Logging library: X
- Fields redacted: list
- Unredacted sensitive fields found: list (file:line)

### Docker / CI injection
- Dockerfile ARG misuse for secrets: yes/no (files affected)
- CI workflow plaintext secrets: yes/no (files affected)

### Recommended immediate actions
1. [CRITICAL] Rotate any secret found in git history before making any other change.
2. ...

Safety

  • Do not print actual secret values found — reference them by variable name and file:line only, redacted to first/last 4 characters if any portion must be shown.
  • Do not modify .env files, secrets manager configuration, or CI secret store entries.
  • Do not run git filter-repo or history rewrite commands; document them for the user to run manually with explicit approval.
  • Do not commit changes to .gitignore or .env.example without explicit user approval.
  • If a Critical live-secret finding appears (value committed to git history), surface it immediately and flag rotation as the blocking action before completing the rest of the review.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Gives 0 of the 12 instructions most project setup skills give in ~3.5k tokens

Counted across 999 of the 1,637 authors here whose files we hold, read 2026-08-07

  • Ask one question at a timein 29 of 999, across 28 files
  • Detect the package manager from lockfilesin 28 of 999, across 9 files
  • Present findings to the userin 26 of 999, across 5 files
  • Explore current repo statein 24 of 999, across 3 files
  • Update the agent skills block in place if it existsin 24 of 999, across 3 files
  • Install husky lint-staged and prettierin 23 of 999, across 4 files
  • Create the lintstagedrc filein 22 of 999, across 3 files
  • Commit all changed filesin 22 of 999, across 3 files
  • Run lint-staged to verify it worksin 22 of 999, across 3 files
  • Create the husky pre-commit filein 21 of 999, across 2 files
  • Create a prettierrc file if missingin 21 of 999, across 2 files
  • Initialize huskyin 21 of 999, across 2 files

Said here and by no other author read

  • scan source code for hardcoded secrets
  • ensure env example template is complete
  • verify env secrets have no default fallbacks
  • confirm logging middleware redacts secrets
  • check secret rotation readiness
  • verify dockerfiles use runtime secret injection

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.

Keep looking

Skills are one crate of 327,069. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.