Autonomous AI Agent Finds CVSS 10.0 Full Compromise in Hoppscotch

infy1 pts0 comments

Mass Assignment via Onboarding Endpoint Allows Unauthenticated JWT_SECRET Overwrite · Advisory · hoppscotch/hoppscotch · GitHub

//repos/advisories/show" data-turbo-transient="true" />

Skip to content

Search or jump to...

Search code, repositories, users, issues, pull requests...

-->

Search

Clear

Search syntax tips

Provide feedback

--><br>We read every piece of feedback, and take your input very seriously.

Include my email address so I can be contacted

Cancel

Submit feedback

Saved searches

Use saved searches to filter your results more quickly

-->

Name

Query

To see all available qualifiers, see our documentation.

Cancel

Create saved search

Sign in

//repos/advisories/show;ref_cta:Sign up;ref_loc:header logged out"}"<br>Sign up

Appearance settings

Resetting focus

You signed in with another tab or window. Reload to refresh your session.<br>You signed out in another tab or window. Reload to refresh your session.<br>You switched accounts on another tab or window. Reload to refresh your session.

Dismiss alert

{{ message }}

hoppscotch

hoppscotch

Public

Uh oh!

There was an error while loading. Please reload this page.

Notifications<br>You must be signed in to change notification settings

Fork<br>5.9k

Star<br>79.6k

Mass Assignment via Onboarding Endpoint Allows Unauthenticated JWT_SECRET Overwrite

Critical

jamesgeorge007<br>published<br>GHSA-j542-4rch-8hwf<br>May 28, 2026

Software

hoppscotch-backend (Self-Hosted)

Affected versions

Patched versions

2026.5.0

Description

Summary

The POST /v1/onboarding/config endpoint allows an unauthenticated attacker to inject arbitrary InfraConfig keys -- including JWT_SECRET and SESSION_SECRET -- into the database via mass assignment. These keys are not declared in the SaveOnboardingConfigRequest DTO , but because the NestJS ValidationPipe does not strip extra properties, they pass through to the service layer where Object.entries(dto) iterates all keys without restriction.

This results in full server compromise: the attacker controls the JWT signing key and can forge tokens for any user including admin.

The attack works only on fresh installs before onboarding completes (or when usersCount === 0). However, self-hosted Hoppscotch instances are exposed to the internet during initial setup, and the window between deployment and onboarding completion is the exact moment the instance is most vulnerable.

Confirmed with a live proof-of-concept on a fresh Hoppscotch AIO Docker deployment.

Affected Components

packages/hoppscotch-backend/src/main.ts (lines 93--97) -- ValidationPipe configuration

packages/hoppscotch-backend/src/infra-config/infra-config.service.ts (lines 538--553) -- unconstrained key iteration

packages/hoppscotch-backend/src/infra-config/infra-config.service.ts (line 806) -- validateEnvValues switch with default: break

packages/hoppscotch-backend/src/infra-config/onboarding.controller.ts (line 58) -- unauthenticated endpoint

packages/hoppscotch-backend/src/types/InfraConfig.ts (lines 5--6) -- JWT_SECRET and SESSION_SECRET as valid InfraConfigEnum values

Root Cause

Four independent weaknesses combine to enable this attack:

Weakness 1 -- ValidationPipe missing whitelist: true (main.ts:93--97)

app.useGlobalPipes(<br>new ValidationPipe({<br>transform: true,<br>// whitelist: true -- MISSING: extra properties are NOT stripped<br>}),<br>);

Without whitelist: true, NestJS copies all properties from the request body to the DTO object, including properties not declared in the SaveOnboardingConfigRequest class. JWT_SECRET, SESSION_SECRET, and other security-critical keys are not DTO fields -- they are extra properties that should be stripped but are not.

Weakness 1 alone is sufficient to block this attack. Weaknesses 2--4 should also be addressed as defense in depth.

Weakness 2 -- Unconstrained Object.entries(dto) (infra-config.service.ts:538--543)

value !== undefined)<br>.map(([key, value]) => ({<br>name: key as InfraConfigEnum, // TypeScript cast, no runtime validation<br>value,<br>})),<br>];">const configEntries: InfraConfigArgs[] = [<br>...Object.entries(dto)<br>.filter(([_, value]) => value !== undefined)<br>.map(([key, value]) => ({<br>name: key as InfraConfigEnum, // TypeScript cast, no runtime validation<br>value,<br>})),<br>];

The cast key as InfraConfigEnum performs no runtime check. Object.entries(dto) iterates every property on the DTO object, including the extra properties that leaked through from Weakness 1. Since JWT_SECRET is a valid InfraConfigEnum value (defined in types/InfraConfig.ts:5), the attacker-supplied key is treated as a legitimate config entry and written to the database.

Weakness 3 -- validateEnvValues has default: break (infra-config.service.ts:806)

The validateEnvValues method uses a switch statement over InfraConfigEnum values to validate incoming config entries. The default case is:

default:<br>break; // unrecognized keys silently pass validation

JWT_SECRET and SESSION_SECRET do not have explicit validation cases in this switch. They fall through to default: break...

hoppscotch config value jwt_secret infra onboarding

Related Articles