The security invariant

Bind the requested object to the authenticated owner at the mutation.

01 / The boundary

A valid session is only half of the authorization decision.

A personal environment is a user-owned object. To change it, a backend needs two facts together: who is making the request, and whether that person owns the selected environment. Hoppscotch’s GraphQL authentication guard established the first fact, but the affected update path did not carry that identity into the operation on the object.

The public advisory covers the self-hosted Hoppscotch backend. It describes unauthorized access, modification and deletion when an authenticated user knows another user’s environment identifier. The mechanism is an insecure direct object reference: the supplied identifier selected a record without the required owner constraint. The backend accepted an object address where it needed an authorization decision.

02 / Mutation path

Identity stopped short of the database operation.

Before the fix, updateUserEnvironment accepted an environment ID, a name and serialized variables. Its database update matched the ID alone. The resolver applied GqlAuthGuard but did not extract the authenticated user for this method. There was no owner value available to constrain the update. The service then constructed a response from the updated object, including its owner, name and variables.

Delete illustrates a different way to lose the same boundary. Its service method already received the caller’s UID and used it to look up that caller’s global environment. That special-case check prevented one kind of deletion; it did not authorize the requested personal environment. The eventual delete still selected by ID alone. Seeing identity somewhere in a method is therefore weaker evidence than seeing it constrain the consequential query.

Identity must travel with the object operation
Before the fix
  1. Authenticated user A

    The request passes the login guard.

  2. Environment ID for B

    The caller supplies another object’s ID.

  3. Match the ID alone

    The mutation does not require owner A.

After the fix
  1. Authenticated user A

    The resolver obtains the trusted user.

  2. ID + owner A

    The service scopes the mutation to both.

  3. No owned match

    Another user’s environment is not changed.

Conceptual two-user example. A and B represent separate accounts on one instance; this is not a replay of a private test.

03 / Source & fix

Put ownership in the mutation’s selection predicate.

The patch adds the authenticated user parameter to the update resolver and passes it to the service. The service requires both the requested environment ID and that user’s UID in the update predicate. Delete receives the corresponding UID constraint. The same commit also adds an owner constraint to the global-environment clearing query.

This makes the security rule visible at the point that changes state. The ID answers which object the caller is asking for; the owner field answers whether it is within that caller’s personal scope. Keeping them in the same database operation avoids relying on an earlier, unrelated lookup as evidence of authorization.

The resolver obtains the owner identity from authenticated request context. An owner field supplied by the caller would defeat that property. The difference is small in the query, but the complete fix spans both layers: carry trusted identity through the resolver, then use it where data changes.

Before · update by IDSource
where: { id: id },
user-environments.service.ts55c1cb82
After · extract the callerSource
@GqlUser() user: User,
user-environments.resolver.tsa1be60da
After · require the ownerSource
where: { id: id, userUid: user.uid },
user-environments.service.tsa1be60da

Selected upstream lines; indentation normalized. Links include surrounding code at immutable revisions.

04 / Impact & conditions

An unpredictable ID reduces discovery, not authority.

The advisory assigns High severity and CVSS 3.1 8.3. It identifies self-hosted backend versions through 2026.1.1 as affected and 2026.2.0 as patched. Environments can hold request credentials and tokens, making unauthorized access or changes consequential. The attacker must already possess a target environment ID; the CUID format makes broad guessing less practical.

A shared instance
A logged-in attacker targets an environment belonging to a different user on that instance.
A known object ID
The advisory requires knowledge of the target identifier; it does not establish unrestricted enumeration.
Sensitive environment data
Practical impact depends on the data stored and how that environment is used.
05 / Review & regression

Prove isolation with two owners and one real data store.

The upstream commit updates service tests to supply the newly required user parameter. Those edits are useful evidence that the calling contract changed. They should not be described as proof of a complete cross-account integration suite: a mock that returns an object for any query can hide a missing ownership constraint.

A focused regression check creates one environment for each of two ordinary accounts in an isolated instance. Keep the object IDs fixed and change the authenticated account. Verify the response and persisted state separately. For a rejected operation, also check that no data or change notification crosses the ownership boundary. The following matrix is a suggested review design, not a claim of tests executed by TraceMint.

Suggested regression checks
ScenarioExpected boundary
Owner updates their environmentThe intended change succeeds and the owner receives the result.
Different account uses the same IDUpdate fails; the original state remains and no environment data is returned.
Different account attempts deletionDeletion fails; the owner’s environment still exists.
Related clear and bulk operationsEach operation enforces its documented owner scope independently.
06 / Lessons & limits

Follow the principal all the way to the write.

Authorization review becomes more reliable when it follows data flow instead of looking only for guard annotations. Start at the authenticated principal, follow each parameter through the resolver and service, and finish at the exact query that reads or changes the object. If the identity disappears before the query, explain where the owner decision is enforced. A route being authenticated is not that explanation.

Review each operation independently. A list endpoint scoped to the current user does not protect a mutation that accepts a separate ID. A check for a global environment does not prove ownership of a personal one. Useful negative tests preserve a valid session and valid input while changing only the relationship between the principal and the target.

The public record credits TraceMint as a Reporter alongside another reporter; the original advisory preserves the complete credit record. Hoppscotch’s maintainers supplied the remediation. This study does not claim sole discovery, identify a private research workflow, or demonstrate access to any live deployment. Its diagrams and review matrix explain the published mechanism.

Source record

Follow the evidence.

Public upstream records and immutable code revisions, reviewed 2026-09-19.

  1. Hoppscotch security advisory

    Scope, prerequisites, impact, fixed release and complete reporter credits.

  2. Service before the fix

    Immutable parent revision · 55c1cb82 · update and delete select by ID.

  3. Upstream corrective commit

    a1be60da · resolver, service and test changes. The commit also addresses a separate onboarding issue.

  4. Resolver after the fix

    Authenticated user extraction and propagation into the update service.

  5. Service after the fix

    Owner constraints in update, delete and clear operations.

  6. Upstream service tests

    Updated calls include the authenticated-user parameter.

  7. Hoppscotch 2026.2.0 release

    Release record links the security fix in pull request #5897.

Brief code excerpts from hoppscotch/hoppscotch. Upstream license: MIT License. All remediation described here is upstream work.

Read the original record

This case study analyzes an already-public advisory and upstream source. For the complete impact, remediation and credit record, see the Hoppscotch security advisory.

Editorial publication is separate from disclosure. No private discovery timeline or method has been inferred.

More credited contributions