Deployed Frontend Secret and Debug Endpoint Exposure Gate
Pricing
from $20.00 / 1,000 run starteds
Deployed Frontend Secret and Debug Endpoint Exposure Gate
Reads the script bundles that your deployed pages load from their own origin and probes a fixed set of debug and schema paths. Reports each leaked key and each exposed endpoint with a redacted match, and gives one gate_pass verdict for your build step. HT
Pricing
from $20.00 / 1,000 run starteds
Rating
0.0
(0)
Developer
kingii98
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 days ago
Last modified
Categories
Share
After each deploy, this Actor answers one question: did this build publish a live API key, and did it leave a debug or a schema endpoint open?
It reads each page that you give it, reads the scripts that the page loads from
its own origin, searches those scripts for secret patterns, and probes a fixed
set of debug and schema paths against the origin. It writes one record for each
finding, one record for each page and one summary record with a gatePass
boolean for your build step.
It uses HTTP only: GET and HEAD requests plus regular expression matching. It
uses no browser, no proxy, no CAPTCHA service, no paid API and no external
database. It never sends a discovered key anywhere, and it never calls a
discovered endpoint with a discovered key. Only the first four characters of a
match reach the dataset.
What it checks
Secrets in script bundles. Private key blocks, Stripe live secret keys, AWS access keys, GitHub tokens, Slack tokens, OpenAI keys, SendGrid keys, npm tokens, Mailgun and Twilio identifiers, signed JSON Web Tokens, and long values that sit next to a secret name.
Exposed endpoints. The default probe set holds 12 paths: /.env,
/.env.local, /.git/config, /actuator/env, /debug/vars, /swagger.json,
/openapi.json, /api-docs, /swagger-ui.html, /graphql, /graphiql and
/server-status.
The false positive rules
A gate that reports a public key is removed from a build step in one week. Three rules keep the report honest:
- A publishable key is never a finding. A Stripe publishable key
(
pk_live_,pk_test_), a Mapbox public token (pk.eyJ), a PostHog project key (phc_) and any value behind apublic-orpublishablename are dropped before the report. - A key type that is public by design keeps a low severity. A Google or
Firebase Web API key (
AIza...) is reported aslow, so it never fails the gate at the default threshold. Restrict such a key by referrer instead. - A probe needs evidence, not a status code. A single page application
answers
200with its own HTML shell for every path. A probe is a finding only when the body proves the exposure:/.envmust look like an environment file,/openapi.jsonmust hold anopenapiorswaggerkey,/graphqlmust answer like a GraphQL server. An HTML answer is refused unless the rule expects HTML and finds its marker.
A documentation example value, for example AKIAIOSFODNN7EXAMPLE, is dropped as
well.
Input
| Field | Type | Default | Meaning |
|---|---|---|---|
urls | array | two public example pages | 1 to 20 page URLs on domains that you own |
ownershipConfirmed | boolean | true | Confirm that you own, or may test, every domain |
scanBundles | boolean | true | Read the same-origin scripts of each page |
maxBundleBytes | integer | 2000000 | Stop the read of one script at this size |
maxScriptsPerPage | integer | 10 | Read at most this many scripts for each page |
probePaths | array | the 12 paths above | Maximum 30 paths |
failOn | string | high | info, low, medium, high, critical or never |
timeoutSeconds | integer | 15 | Timeout of one request |
maxRequests | integer | 400 | Request budget of the run |
maxRedirects | integer | 2 | Same-origin redirect hops for a page request |
trackBaseline | boolean | false | Mark each finding with isNew against the last run |
baselineKey | string | baseline | Key of the baseline record in the key-value store |
Every field has a default, so a run with empty input {} succeeds.
Only same-origin scripts are read. A script on a content delivery network belongs
to another party, so the Actor leaves it alone and counts it in
scriptsSkippedCrossOrigin.
Output
Finding record — recordType: "finding", pageUrl, sourceUrl,
sourceType (bundle, inline_script, page or probe), findingType
(secret_pattern or exposed_schema_endpoint), patternName (for example
stripe_live_secret_key or exposed_env_file), redactedMatch (the first four
characters plus the length), byteOffset, severity, note, fingerprint,
isNew and foundAt.
Page record — recordType: "page", pageUrl, finalUrl, httpStatus,
scriptsRead, bytesRead, inlineScripts, scriptsSkippedCrossOrigin,
scriptsSkippedOverLimit, probesRun, findingCount, error and auditedAt.
Summary record — recordType: "summary", gatePass, failOn,
pagesRequested, pagesAudited, pagesSkipped, scriptsRead,
bundlesScanned, bytesRead, findingCount, newFindingCount,
findingsBySeverity, requestsSent, status, note and finishedAt.
Use it in a build step
Run the Actor after each deploy and read the last record of the dataset:
GATE=$(curl -s "https://api.apify.com/v2/datasets/$DATASET_ID/items?clean=true" \| jq -r '[.[] | select(.recordType=="summary")][0].gatePass')[ "$GATE" = "true" ] || exit 1
Set trackBaseline to true to accept a known false positive once. The first
run stores the fingerprint of every finding. The next run marks a finding that
the store does not hold with isNew: true, so your step can fail on new findings
only. A fingerprint holds the origin, the finding type, the pattern name and the
redacted value, so it survives a new bundle name after the next build.
A business verdict is never a failed run
A failed gate, a page that does not answer, a target that the Actor refuses and a
run with zero findings are all results. Each one is a dataset record plus a run
status message, and the run ends SUCCEEDED. A run ends FAILED only when the
input itself cannot be parsed.
Safety
The Actor refuses every loopback, private, link-local and reserved address, and refuses a URL that carries credentials. It re-checks the address after each redirect hop, and it does not follow a redirect that leaves the origin. A script read and a probe never follow a redirect.
Set ownershipConfirmed to false and the Actor audits nothing: it probes debug
paths, so it must run against your own deployment only.
Pricing (pay per event)
| Event | Unit | Price |
|---|---|---|
run-started | one Actor run | USD 0.02 |
page-audited | one page fetched, parsed and probed with the path set | USD 0.03 |
bundle-scanned | one script file read and searched | USD 0.01 |
finding-reported | one finding written to the dataset | USD 0.02 |
run-started is charged once, after the input is accepted. page-audited is
charged for each page that the Actor really fetched and probed; a page that was
refused or unreachable is not charged. bundle-scanned is charged for each
script that was read and searched; an inline script costs nothing extra, because
the page body already holds it. finding-reported is charged for each finding
record. A single page application with 6 bundles and no findings costs about
USD 0.11 for each deploy.
Local development
uv syncuv run pytestuv run ruff check .