Unit 1: Secrets in code · Lesson 1 of 4 · 18 min

Find the secrets in a repository

Scan quay-api for credentials, then work out why a staging password slipped past .gitignore.

mission

You are
You are the application security engineer at Quayside.
Situation
quay-api is about to be shared with an outside contractor. Before anyone outside the company gets a copy, you check what secrets sit in the code.
Goal
List every credential in the working tree and explain how each one got there.
Hand in
A short list of findings with file, type and who added it.

A secret scanner as a baggage X-ray

01

Look around the repository

learner@dev-lab
ls -a
.
..
.git
.github
.gitignore
.npmrc
README.md
config
docker
package-lock.json
package.json
src
test
tests
git log --oneline | wc -l
10

you should see A Node.js service with config, docker and tests folders and ten commits of history.

ls
Lists the files in a folder. Built into Linux and macOS.
git
Tracks changes to code and shows its history. Install with sudo apt install git, or brew install git on a Mac.
wc
Counts lines, words and bytes. Built into Linux and macOS.
02

Read the scanner's rules

gitleaks is a free, open source secret scanner. Install it with brew install gitleaks or from the gitleaks releases page on GitHub. Quayside's ~/gitleaks.toml keeps the built-in rules and adds two of its own: one for its payments key format and one for passwords in config files.

learner@dev-lab
grep -E '^(id|regex) ' gitleaks.toml
id = "quay-live-key"
regex = '''qsk_live_[A-Za-z0-9]{24}'''
id = "password-in-config"
regex = '''(?i)[a-z_]*(?:password|passwd|pwd)\s*[=:]\s*["']?([^\s"',;]{6,})'''

you should see Two extra rules, each a regular expression.

grep
Prints the lines of text that match a pattern. Built into Linux and macOS.
03

Run the scanner

gitleaks dir scans the files on disk. -f json -r - prints the report as JSON and jq turns it into one line per finding: file, line and rule. -v --redact prints a finding in full without showing the secret.

learner@dev-lab
gitleaks dir . -c ~/gitleaks.toml --no-banner -l error -f json -r - | jq -r 'sort_by(.File)[] | [.File, .StartLine, .RuleID] | @tsv'
docker/.env.staging	3	password-in-config
tests/fixtures/deploy_key	1	private-key
gitleaks dir docker/.env.staging -c ~/gitleaks.toml --no-banner --no-color --redact -v
Finding:     DB_PASSWORD=REDACTED
Secret:      REDACTED
RuleID:      password-in-config
Entropy:     3.916127
File:        docker/.env.staging
Line:        3
Fingerprint: docker/.env.staging:password-in-config:3

6:18PM INF scanned ~84 bytes (84 bytes) in 3.5ms
6:18PM WRN leaks found: 1

you should see A staging database password and a private key, both committed as ordinary files.

defender tip gitleaks ships well over a hundred rules for cloud, payment and chat tokens. trufflehog and GitHub secret scanning can also check whether a found key still works.

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.
04

See who added the password

learner@dev-lab
cat docker/.env.staging
DB_HOST=db.staging.quay.internal
DB_USER=quay_app
DB_PASSWORD=Harbour-Staging-2026!
git log --format='%h %an %ad %s' --date=short -- docker/.env.staging
e70bc03 Ines Rocha 2026-09-18 Add staging config and test fixtures

you should see Ines added a staging config file with a real database password on 18 September.

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

Find out why .gitignore missed it

git check-ignore -v prints the rule that ignores a path, or nothing when no rule matches.

learner@dev-lab
cat .gitignore
node_modules/
.env
git check-ignore -v docker/.env.staging; echo "exit $?"
exit 1

you should see The rule .env matches only a file named exactly .env, so .env.staging was committed.

defender tip Ignore patterns are an easy place for gaps. Use .env* and keep a committed .env.example with placeholder values instead.

echo
Prints its arguments. Built into Linux and macOS.

What a defender takes away

  1. 1Scan every repository you inherit before you share or publish it.
  2. 2Secrets hide in configs, test fixtures and docker files, not just in code.
  3. 3Check ignore rules with git check-ignore, do not assume them.

Questions

0 of 3 solved

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

  1. Show hint

    Run the scan step in ~/repos/quay-api and count the lines.

  2. Show hint

    Look at the private-key finding.

  3. Show hint

    `git log --format='%an' -- docker/.env.staging`