---
title: "Your recorded test might be committing a session cookie"
description: "Record a real request, save it as a test, and the cookie, bearer token, and SSN ride along into git. Hover strips credentials out of the request before it writes the spec, and tells you what it dropped."
date: 2026-06-09
author: "Hover"
tags: [security, playwright, secrets, regression]
---

# Your recorded test might be committing a session cookie

The fastest way to write an API test is to record a real request and replay it. The fastest way to leak a credential is the same move. The request you just captured carries a live session cookie, an `Authorization: Bearer` header, maybe an SSN or a card number in the JSON body. Save it verbatim as a spec and all of that lands in git, in plain text, in a file other people clone.

It happens because the convenient path and the safe path look identical at the moment you hit save. The request works. The test passes. The secret is three lines down where nobody looks until it shows up in a leaked-credentials scan.

## What Hover strips before it writes

When Hover crystallizes a recorded request into a committed `.security.spec.ts`, it runs the request through a sanitizer first. Three passes:

Credential-bearing headers get dropped outright: `cookie`, `set-cookie`, `authorization`, `proxy-authorization`, `x-api-key`, `x-auth-token`, `x-amz-security-token`. They don't get masked, they get removed, because CI auth comes from a Playwright `storageState` fixture, not from a string baked into the file.

Query-string values that name a credential get masked: a `?token=...` or `?api_key=...` in the URL is a common leak, so the value comes out and a placeholder goes in.

JSON body fields get masked by key name: `password`, `token`, `secret`, `api_key`, `access_token`, `auth`, `ssn`, `credit_card`, and the spelling variants. The matcher covers every JSON value type, not just strings. An SSN sent as a number (`"ssn": 123456789`) used to slip past a string-only matcher straight into the committed file. It doesn't anymore.

## One alternation, so coverage can't drift

There's a sharp edge here worth calling out. An early version kept two separate lists, one for URL params and one for body fields. They drifted: `auth` and `apikey` were in the URL list and missing from the body list, so a body field named `auth` leaked while the same name in the query string got caught. Two lists that are "supposed to match" eventually don't.

Now both matchers derive from a single alternation. Add a credential name once and the URL pass and the body pass both pick it up. You can't fix one and forget the other, because there's only one to fix.

## You see what it dropped

The sanitizer returns the list of header and field names it stripped or masked, and Hover surfaces it. Saving a spec isn't a silent rewrite. You get told "dropped: cookie, authorization; masked: ssn" so you know the regression test is real and the secret isn't in it. Auth comes back at run time from the fixture, the assertions run the same, and the file is safe to commit.

This is the unglamorous half of the [explore-then-crystallize](/blog/ai-authored-playwright-tests) idea. An AI authoring tests is only useful if the artifact it leaves behind is one you'd actually commit. A spec with a live token in it is not that artifact, so the sanitizer runs on the way out, every time.
