Skip to content

CWE-615: Inclusion of Sensitive Information in Source Code Comments

Overview

Sensitive information left in comments - passwords, API keys, internal IP addresses, security TODOs, or PII - is exposed to anyone who can read the source. For code that runs in the browser, that is every visitor: JavaScript comments ship to the client and are visible via "View Source." Removing the comment from the current file is not enough: version control preserves every prior revision, so the data stays recoverable from history.

Relationship to Other CWEs

This page's MITRE parent, CWE-540, has no page here, so the bullets below reach the corpus through its grandparent CWE-538. MITRE records no direct relationship between CWE-615 and any page in this corpus.

OWASP Classification

A01:2025 - Broken Access Control

Risk

Medium-High: A credential in a comment is usable as it stands, with no further weakness to exploit; internal addresses and a TODO naming an unfixed vulnerability tell an attacker where to aim next; PII is disclosed to everyone who can read the file. How large that audience is depends on where the comment lives - repository access for server-side code, every visitor for comments shipped in client-side JavaScript.

Remediation Steps

Core Principle: Never place credentials, internal infrastructure details, or descriptions of unfixed vulnerabilities in comments; treat any comment as visible to anyone who can read the source, including end users of client-side code.

Locate Sensitive Information in Comments

Search the comments for:

  • Credentials: passwords, API keys, database connection strings
  • Internal infrastructure: IP addresses, server names, internal URLs
  • Security TODOs that describe a known, unfixed vulnerability
  • PII: SSNs, credit card numbers, real personal data used as test fixtures
  • Commented-out code holding credentials that were disabled rather than deleted
grep -riE "password|api[_-]?key|secret|token" --include="*.java" --include="*.py" --include="*.js" .
grep -riE "TODO.*(security|vuln)|FIXME.*inject|HACK" .
grep -rE "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" . | grep -E "#|//|/\*"

Remove Sensitive Comments and Fix Underlying Issues (Primary Defense)

  • Delete commented-out code that contains real credentials rather than leaving it in place. Commenting the code out is not the same as removing the secret, and "temporary" comments become permanent.
  • Fix the underlying issue first, then remove the security TODO. A comment describing an unfixed vulnerability tells an attacker where to look.
  • Keep documentation that has to exist - setup notes, architecture context - in a docs repository or wiki rather than in comments shipped with the code.
  • Treat client-side code as public. JavaScript comments ship to every browser and are visible via "View Source" or DevTools regardless of repository access controls.

Clean Sensitive Data from Version-Control History

Deleting a comment from the current revision does not remove it from history - anyone with repository access can retrieve it with standard version-control commands.

# Search history for previously committed secrets
git log -p | grep -i "password"
git log -S "API_KEY" --all
git log --all --full-history -- "**/secrets.py"

# Rewrite history to purge a secret (disruptive - see warning below)
# patterns.txt holds one replacement per line, e.g.:  P@ssw0rd123==>REMOVED
git clone <repo-url> repo-rewrite && cd repo-rewrite  # filter-repo wants a fresh clone; otherwise pass --force
git filter-repo --replace-text ../patterns.txt        # replaces the secret in place, keeps the file
git remote add origin <repo-url>                      # filter-repo deletes the remote by design after a rewrite
git push --force --all
git push --force --tags

Use --replace-text, not --path <file> --invert-paths: the latter deletes the whole file from every commit, which is the wrong tool when the secret sits in a comment inside a source file you want to keep. (bfg --replace-text patterns.txt does the same substitution if you already have BFG in your toolchain.) The git remote add line is not optional: git filter-repo removes origin after a rewrite so a stale remote cannot be pushed to by accident. Without it, the force push has no destination.

Warning: Rewriting history is disruptive. Every clone must be discarded and re-cloned, open pull requests break, and forks or backups made before the rewrite still contain the data. Treat any credential that was ever committed as compromised and rotate it immediately; history rewriting is cleanup, not the fix.

Prevent Future Sensitive Comments (Defense in Depth)

  • Use pre-commit hooks to block commits containing likely secret patterns before they reach history.
  • Run a secret-detection tool (git-secrets, gitleaks, TruffleHog) in CI on every push, not just locally.
  • Treat a sensitive comment as a blocking review finding, the same as a hardcoded credential.
gitleaks detect --source . --verbose
git secrets --scan-history

Test the Fix

  • Re-run the credential and TODO search patterns above and confirm no real secrets remain
  • Confirm the pre-commit hook rejects a test commit containing a fake secret pattern
  • Re-scan with the security scanner to confirm the finding is resolved

Common Vulnerable Patterns

// VULNERABLE - pseudo-code
// Database password: P@ssw0rd123
// Production server: 10.0.1.50, admin panel: https://internal.example.com/admin
// TODO: this query is vulnerable to injection, fix later
// Test user: john.doe@example.com / SSN 123-45-6789
config = { host: "localhost", user: "admin" }  // password: "admin123" - old, still visible

Why this is vulnerable: Live credentials, internal topology, an admitted-but-unfixed vulnerability, and real personal data are all readable by anyone with source access. The security TODO also confirms the weakness exists and points an attacker at the code that carries it.

Secure Patterns

// SECURE - pseudo-code
config = {
    host: env("DB_HOST"),
    user: env("DB_USER"),
    password: secrets_manager.get("prod/db/password")
}
// See internal docs for setup instructions - not inlined here

Why this works: Configuration values are read from environment variables or a secrets manager at runtime instead of being written into the source, so there is no secret in the file for a comment - or the code itself - to leak. Setup documentation lives in a separate, access-controlled location rather than shipping with every checkout.

Additional Resources