Your deploy tool wrote your secrets to a file, then you shipped the file
curl -s https://yoursite.com/.serverless/cloudformation-template-update-stack.json. If that comes back with JSON instead of a 404, stop reading and go look at what's in it, because the answer is probably your Lambda environment variables, in plaintext, with the secret references already resolved to their real values.
This is a different kind of exposed file from the .env everyone knows to block. Nobody wrote these by hand. They're the output of your deploy tooling: the intermediate files Serverless Framework, the AWS CDK, and Spring Boot's build plugins generate on the way to shipping. They're meant to live for a moment in a build directory and then be irrelevant. The failure is that the build directory ends up inside the published web root, so the whole .serverless/ or cdk.out/ folder gets uploaded alongside index.html and starts answering anonymous GETs. Each tool leaks something different, so here's what each one hands over.
Serverless Framework resolves your secrets into the template
This is the sharp one. When you write a serverless.yml, you reference secrets indirectly, pulling a database URL from SSM or an API key from Secrets Manager so the value never sits in your repo. That's the right pattern. The trap is what serverless deploy does next: to build the CloudFormation stack, it resolves every one of those references into its literal value and writes the result into .serverless/cloudformation-template-update-stack.json, inside the Lambda function's Environment.Variables block. The indirection that kept the secret out of your source code is gone in the build output. The real values are right there.
The sibling file, .serverless/serverless-state.json, carries the same resolved environment in the deploy state for the AWS provider. The check fires on either one, and only when the file genuinely contains resolved secrets: it gates on the CloudFormation marker AWSTemplateFormatVersion (or the Serverless service and provider: aws structure) appearing together with a populated "Variables" block holding real "KEY": "value" literals. A template that only has unresolved references, or no variables at all, doesn't trip it. The finding means actual credentials are sitting in the file.
The CDK writes your whole infrastructure map
The AWS CDK synthesizes your infrastructure code into a cloud assembly, a cdk.out/ directory, and at the top of it sits manifest.json. This one usually isn't a secret leak. It's a reconnaissance leak, which is why it's rated high rather than critical.
manifest.json is the index of your entire deployment: every CloudFormation stack, the artifacts each one produces, the dependencies between them, the asset hashes, the environment each targets. For an attacker, it's a free map of how your infrastructure is wired, the names and relationships you'd otherwise have to guess at. It hands over the floor plan, which is the work that usually comes before anyone tries a door.
The gate looks for the CDK-internal artifact type aws:cloudformation:stack alongside the manifest's schema version, so a coincidental manifest.json from something else won't match. It reads enough to confirm it's a real cloud-assembly manifest, then stops.
git.properties only fires when it actually carries a credential
Spring Boot's git-commit-id Maven/Gradle plugin writes a git.properties file into the build so the app can report which commit it's running. Most of the time that file is harmless: a commit hash, a build timestamp, a branch name. Recon-grade at worst, and not worth flagging on its own.
There's one configuration where it turns dangerous. If the plugin is set to include git.remote.origin.url and the repo was cloned over HTTPS with credentials baked into the URL, the file ends up holding git.remote.origin.url=https://user:token@host/repo.git, a working set of repo credentials served to anyone who asks. That's the case the check is built for, and the only case it fires on. The gate requires the plugin's own git.commit.id key to co-occur with a git.remote.origin.url carrying an inline user:token@ credential. A clean, credential-free git.properties is recon, deliberately not flagged, so the finding always means a real login leaked.
Why they all end up downloadable, and the fix
It's the same root cause behind the cloud and infra config files in your web root: the deploy shipped the build directory, not just the build output. A COPY . . in a Dockerfile bakes .serverless/ and cdk.out/ into the image. An rsync without an exclude list copies the whole working tree. The web server then sees a static JSON file next to index.html and serves it, 200, no questions, because it has no idea the file is the resolved guts of your deploy.
The fix is two layers. First, don't deploy these directories at all: .serverless/, cdk.out/, and the build's git.properties are intermediate artifacts that have no reason to be in your served document root. Put them on the exclude list, or build in a stage whose output is only the application. Second, as a backstop, make the server refuse them even if one slips through.
/var/www/html/ index.html .serverless/ <-- resolved Lambda secrets cdk.out/manifest.json <-- full infra topology git.properties <-- repo URL with credentials
And if any of these was reachable, the secrets in it are compromised. The resolved Lambda env vars, the repo token, treat them as already stolen and rotate. The file has been public for as long as it's been deployed.
Whether your site answers these paths is something a stranger settles with a GET, which is exactly the angle SurfaceCheckr takes: from outside, no login, each path gated on its real content signature so a soft-404 catch-all that 200s everything won't produce a false hit. It confirms the file is genuinely there, with genuine secrets or a genuine credential in it, and reports that. It won't use what it finds, because the question it's answering is the one a stranger asks first: is the deploy folder sitting on the doormat?
Read next
- Can anyone download your .env file? (Type the URL and find out)The files you forgot you deployed
- Why a leftover backup.sql is the worst file on your serverThe files you forgot you deployed
- Spring Boot Actuator: the /actuator endpoints that dump your secretsThe files you forgot you deployed
- Is your TLS private key downloadable? (It's the one file that undoes HTTPS)The files you forgot you deployed
Find it before someone else does.
Paste your domain. The grade and issue count are free, and you'll see in a couple of minutes exactly what's reachable from outside.