All resources
// Resources

Secrets in Source Code: Review and Remediation Checklist

Published July 2, 2026

A committed credential is an exposure event, even when repository visibility is limited. Copies can exist in forks, caches, build logs, release bundles, developer machines, and Git history. CWE-798 defines hard-coded credentials as passwords or cryptographic keys contained by a product. Its mitigation guidance says to keep outbound credentials outside code and protect them with restrictive access. OWASP’s Secrets Management guidance similarly calls for lifecycle management, least privilege, auditing, rotation, revocation, and expiration.

Start with a containment decision

Do not begin by deleting the line or rewriting history. First establish whether value was real, which service accepted it, when it was valid, and what permission it held. Treat an active committed value as compromised until evidence says otherwise. A placeholder such as example.invalid is not an incident; a token copied from a CI variable may be. Never paste suspected value into ticket, chat, scan output, or this worksheet.

Evidence fieldCompleted artifactOwnerPass / fail
Finding IDSEC-2026-041 with repository path and commit SHASecurity ownerPass: record has no secret value
Credential inventoryissuer, subject, environment, privilege, expiryService ownerFail: issuer or privilege unknown
Revocation proofissuer audit event and denied test with retired credentialIdentity ownerPass: retired credential rejected
Replacement proofdeployment uses new secret reference, not literalRelease ownerPass: build and smoke test succeed
History decisionrisk-approved retain or controlled rewrite recordRepository ownerFail: history rewrite before revocation

The completed artifact is a time-stamped incident record containing identifiers and evidence links, never raw credentials. This separates traceability from disclosure.

Replace literal configuration with runtime injection

Bad code ties secret to source and often to every built artifact:

const webhookToken = "live-token-do-not-commit";
await fetch(url, { headers: { Authorization: `Bearer ${webhookToken}` } });

Use platform-provided runtime configuration and fail closed when missing:

const webhookToken = process.env.WEBHOOK_TOKEN;
if (!webhookToken) throw new Error("WEBHOOK_TOKEN is not configured");
await fetch(url, { headers: { Authorization: `Bearer ${webhookToken}` } });

Environment injection does not make a secret safe by itself: process inspection, crash reports, and broad CI logs can still expose it. Use a managed secret mechanism or protected deployment binding where available, grant workload identity only needed access, and avoid printing environment objects. OWASP recommends centralized, standardized secret handling and least privilege. NIST SP 800-57 frames cryptographic key management as a lifecycle, not a one-time storage choice.

Review places scanners miss

Search code, infrastructure definitions, test fixtures, mobile resources, example files, package archives, container layers, CI variables, generated documentation, and deployment logs. Review use sites too: a key can be absent from source while a debug statement prints it. Verify binaries because client-side bundles make embedded values recoverable. Scanner matches are leads; they do not prove a value is valid, privileged, or even a secret.

Exception flow

A legacy integration may require static credential while replacement work proceeds. Owner opens a dated exception with affected service, reason, least-privilege scope, compensating control, security approver, expiry, and remediation ticket. Example: EXC-SEC-019 permits one read-only partner API token in protected runtime configuration for 14 days while workload identity is enabled. Exception fails closed at expiry: revoke token or renew approval with fresh evidence. It never authorizes committing credential to source.

Remediate in safe order

  1. Disable or revoke exposed credential at issuer; rotate dependent configuration.
  2. Validate replacement through intended runtime path and confirm old credential is denied.
  3. Inspect audit events for use since exposure; preserve relevant records under incident process.
  4. Remove literal from current source, artifacts, examples, and logs. Rewrite history only after containment and coordination because it disrupts collaborators without revoking copied value.
  5. Add pre-commit and CI secret detection, then test detector with synthetic marker only.

Acceptance passes when no active literal remains in reviewed delivery paths, replacement is least-privileged, retired value is rejected, evidence record is complete, and exception has expiry or closure. Failure triggers incident ownership, credential revocation, scoped investigation, and retest. Source-code review can trace secret use from configuration boundary to outbound call.

Verify replacement boundary

Deploy replacement to isolated test environment, invoke only intended workload identity, and confirm issuer audit log records new subject without value. Then call same integration with revoked reference: expected outcome is denied authentication, not retry using embedded fallback. Preserve request ID, secret identifier, issuer event, deployment revision, owner, and result. A failed rotation needs rollback plan that changes secret reference, never reactivates compromised credential.

Worked review and closure

Trace one credential from creation through runtime use and retirement. Suppose worker needs database token. Deployment should receive secret through platform binding, code should read binding at execution, and logs must contain neither value nor derived authorization header. Test a deliberately invalid synthetic value in staging with grep -R "test-secret-do-not-use" .; expected output has no repository match after removal. Inspect built artifact and source-map policy too, because a clean source tree does not prove bundle is clean.

Review failure path. An exception such as missing binding may name binding key, but must not serialize environment object, request headers, connection URL, or token prefix. A rotation can briefly leave old and new values valid; define owner, overlap window, rollback condition, and revocation verification. Avoid placing long-lived secrets in mobile applications: extraction of application package makes embedded value public even when obfuscated.

Closure evidence includes secret identifier or opaque reference, repository scan date, CI scan result, runtime binding check using synthetic credential, rotation owner, and revocation record. Never paste secret value into ticket, screenshot, test log, or review comment. Reopen review when access path, deployment platform, dependency, or principal changes.

Sources

Have a system that needs testing?