Part 1 of 3 in Git Tales Series

Overview

In my recent bug hunting, I stumbled across something that perfectly shows why "deleted" doesn't always mean "gone." I discovered a particularly critical vulnerability that highlights a common oversight among developers, the misunderstanding of Git history. The target codebase had undergone multiple revisions, and one configuration file "appsettings.json" had been deleted in a recent commit.

Curious, I ran a quick scan of the Git history using tools like git log and git diff. To my surprise, the deleted file had once contained Shopify Admin API keys — plaintext credentials capable of granting administrative access to a Shopify store.

This wasn’t a file accidentally left in the final commit. It was a deleted file that had been committed earlier, still lingering in the repository’s history. This sort of vulnerability is often overlooked because many developers assume that once a file is deleted and pushed, it's gone. Git, however, retains every commit unless explicitly scrubbed.

Impact of the Vulnerability

The leaked Admin API token for Shopify was still valid when discovered and belonged to a store that had gone live in January 2025. The potential impact was significant and included:

Full Administrative Access 🔓

  • Read, update, and delete orders, products, customers, inventory, and themes.

  • Manage discount codes and price rules.

  • Add malicious ScriptTags to inject JavaScript into the storefront.

Data Breach Risk 📊

  • Exposed customer PII (names, emails, order history).
  • Violated data protection regulations like the GDPR.

Customer PII

Disruption of Store Operations 🛠

  • Unauthorized configuration changes
  • Loss or corruption of data
  • Deletion of products or categories

In short, a valid Admin API key, even for a development store, has access to production-level functionality. This creates an enormous attack surface.

Proof of Concept (PoC) and Bounty

To responsibly demonstrate the impact, I conducted a limited set of read-only API calls using curl

Get store information

curl -X GET \
  -H "X-Shopify-Access-Token: " \
  https://.myshopify.com/admin/api/2024-01/shop.json

List all products

curl -X GET \
  -H "X-Shopify-Access-Token: " \
  https://.myshopify.com/admin/api/2024-01/products.json

View access scopes

curl -X GET \
  -H "X-Shopify-Access-Token: " \
  https://.myshopify.com/admin/oauth/access_scopes.json

The last API call (access_scopes.json) confirmed that the token had read permissions across major endpoints as shown in the diagram below.

Access scopes

Responsible Disclosure ✅

The vulnerability was reported via the appropriate bug bounty platform. The organization responded quickly, revoked the token, and awarded a bounty for the finding.

Properly Remove Secrets from Git History

Many developers know how to delete a file from a repo, but far fewer understand that Git never forgets. A simple git rm followed by a commit only removes the file from the working directory — not from history.

Step-by-Step: Clean Your Git History 🧼

Option 1: Using BFG Repo-Cleaner

# Delete a file from all history
bfg --delete-files config.json

# Remove secrets from history using a list
bfg --replace-text passwords.txt

Option 2: Using git filter-branch
Kindly note this method is slower but more flexible.

git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch config.json" \
  --prune-empty --tag-name-filter cat -- --all

Important

  • Force-push after cleaning. Commands below:
git push origin --force --all
git push origin --force --tags
  • Rotate any secrets found in history. Very Important!!!!!!!!!
  • Re-clone the repository to avoid residuals.
  • Notify all collaborators so they can do the same.

Paranoid Elliot


Conclusion

This finding is a prime example of how code history can betray your security efforts. Developers often overlook deleted files, unaware that a sensitive credential committed even once can remain accessible unless explicitly purged.

Key Takeaways

  • Deleting a file doesn't delete it from Git history.

  • Admin API keys in Shopify offer massive control and should be treated like passwords.

  • Always audit code and Git history before pushing public repositories or publishing code.

  • Use automated scanners during CI/CD to catch secrets before they go live.


This is just the beginning. In the next parts of the Git Tales series, we’ll dive into:

🧩 Part 2: How Service account keys can be your demons in the cloud.
📝 Part 3: How committed Github tokens can expose your private repos.

Elliot

Until then, treat your Git repo like an open diary — someone might be reading.