Unit 1: Secrets in code · Lesson 2 of 4 · 20 min

Deleted is not gone

A payments key was removed from the code a week after it was added. Prove it is still in the history and work out who could have seen it.

case file

Toyota T-Connect Key Exposed on GitHub

Toyota Motor Corporation · 2022

In October 2022 Toyota disclosed that part of the T-Connect source code, published to a public GitHub repository by a development subcontractor, contained an access key to a customer data server. The key was exposed from December 2017 until September 15, 2022, putting email addresses and customer management numbers of 296,019 customers at risk. Toyota could not rule out third-party access and rotated the keys on September 17, 2022.

What went wrong

A hard-coded credential in source code that a contractor published publicly, with no secret scanning or key rotation catching it for nearly five years.

What would have stopped it

  • Never store credentials in source code; use a secrets manager and inject at runtime.
  • Enable secret scanning and push protection on all repositories, including those of contractors.
  • Rotate keys regularly so any leaked key has a short useful life.
  • Contractually require and audit secure development practices for subcontractors.

What defenders could have seen

  • Secret-scanning alert for credentials in a public repository.
  • Database access using the key from IP addresses outside expected application hosts.
  • Periodic search of public code hosting for company domains and key patterns.

sourcesbleepingcomputer.comsecurityaffairs.com

mission

You are
You are the application security engineer at Quayside.
Situation
A developer says the payments key was removed in September, so there is nothing to worry about. The repository was public for a week in between.
Goal
Show whether the key can still be recovered and when it was exposed.
Hand in
The commit that added the key, the exposure window and a recommendation.

Git history as a stack of glass plates

01

Check the current file

learner@dev-lab
cat config/payments.js
module.exports = {
  endpoint: "https://payments.example/v2",
  apiKey: process.env.PAYMENTS_API_KEY,
};

you should see The key is read from the environment now. The working tree looks clean.

cat
Prints the contents of one or more files. Built into Linux and macOS.
02

Scan every commit

gitleaks git reads every added line in every commit, which is where removed secrets still live. jq prints the short commit hash, date, file and rule for each finding.

learner@dev-lab
gitleaks git . -c ~/gitleaks.toml --no-banner -l error -f json -r - | jq -r 'sort_by(.Date, .File)[] | [.Commit[:7], .Date[:10], .File, .RuleID] | @tsv'
9ced0cf	2026-09-03	config/payments.js	quay-live-key
e70bc03	2026-09-18	docker/.env.staging	password-in-config
e70bc03	2026-09-18	tests/fixtures/deploy_key	private-key

you should see The live payments key appears in commit 9ced0cf, even though no current file contains it.

gitleaks
Scans files and git history for secrets such as API keys and passwords. Install with brew install gitleaks, or from its GitHub releases page.
jq
Picks fields out of JSON and reshapes it. Install with sudo apt install jq, or brew install jq on a Mac.
03

Find the add and the remove

git log -S finds commits that change how often a string appears. It is the fastest way to bracket a leak.

learner@dev-lab
git log -S qsk_live_ --format='%h %ad %an %s' --date=short
af475f4 2026-09-10 Maya Chen Move payments key to environment
9ced0cf 2026-09-03 Dev Patel Add payments client
git log -p -S qsk_live_ -- config/payments.js | grep qsk_live_
-  apiKey: "qsk_live_7Hq2Lm4Xc9Vt1Rb8Nw3Pz6Ka",
+  apiKey: "qsk_live_7Hq2Lm4Xc9Vt1Rb8Nw3Pz6Ka",

you should see Added on 3 September, removed on 10 September. Anyone with a clone from that week has the key forever.

git
Tracks changes to code and shows its history. Install with sudo apt install git, or brew install git on a Mac.
grep
Prints the lines of text that match a pattern. Built into Linux and macOS.
04

Work out who could have seen it

learner@dev-lab
cat logs/repo-visibility.log
2026-09-01T09:00:00Z quay-api created visibility=private
2026-09-05T17:58:00Z quay-api visibility changed to public by dev@quay.example
2026-09-12T08:55:00Z quay-api visibility changed to private by maya@quay.example

you should see The repository was public from 5 to 12 September, so the key was on the internet for most of a week.

defender tip Rewriting history with git filter-repo removes the key from future clones, but not from forks, caches or clones made while it was public. Rotation is the fix; rewriting is cleanup.

Two ways to handle a leaked key

Delete and move on
git rm config/payments.js
git commit -m "remove key"

key still valid
key still in history and every clone

The key works for anyone who copied it.

Rotate, then clean up
1. revoke the key at the provider
2. issue a new key into the secrets store
3. check provider logs for use of the old key
4. rewrite history if the repo stays shared

The leaked value becomes useless.

What a defender takes away

  1. 1Removing a secret in a new commit leaves it in history.
  2. 2Scan history, not just the working tree.
  3. 3A leaked key is fixed by revoking it, never by deleting the file.

Questions

0 of 3 solved

Answer each one from what you find on your lab machine.

  1. Show hint

    Run `gitleaks git` with ~/gitleaks.toml in ~/repos/quay-api.

  2. Show hint

    `cat ~/logs/repo-visibility.log`

  3. Show hint

    `git log -S qsk_live_ --oneline`