Skip to content

[Bug]: @sapiom/fetch loses Request.__sapiom metadata when cloning input #690

Description

@Kewe63

Affected package or area

@sapiom/fetch

Affected version, release, or commit

@sapiom/fetch 0.5.0, commit d7d480a

Environment

  • Node.js: v24.14.1
  • Package manager and version: pnpm 10.34.3 via Corepack
  • Operating system: WSL2 / Linux 5.15.167.4-microsoft-standard-WSL2 x86_64
  • Runtime or framework (if relevant): native Fetch API / Request

Minimal reproduction and steps

Reproduction code sample:

const r = new Request("https://example.test/public");
r.__sapiom = { enabled: false };

const copy = new Request(r);

console.log(JSON.stringify({
  original: r.__sapiom,
  cloned: copy.__sapiom ?? null,
  clonedHasOwn: Object.prototype.hasOwnProperty.call(copy, "__sapiom"),
}, null, 2));

Steps:

  1. Create a Request object.
  2. Attach __sapiom = { enabled: false } to it.
  3. Clone it with new Request(request), matching what @sapiom/fetch does internally.
  4. Check whether the cloned request still has __sapiom.

Actual output from the reproduction:

{
  "original": {
    "enabled": false
  },
  "cloned": null,
  "clonedHasOwn": false
}

The metadata is lost during cloning, so @sapiom/fetch reads no per-request override after new Request(input, init).

Expected behavior

The documented per-request override should be honored for Request inputs.

When a request is created like this:

const publicRequest = new Request("/api/public");
(publicRequest as any).__sapiom = { enabled: false };
await fetch(publicRequest);

@sapiom/fetch should bypass Sapiom authorization/payment handling for that single request.

The metadata should be read from the original Request before cloning, or copied onto the cloned request before interceptors run.

Actual behavior

@sapiom/fetch clones the input with new Request(input, init) before reading __sapiom.

Native Request cloning does not preserve custom properties, so __sapiom is lost. As a result, a request with __sapiom = { enabled: false } is treated as if no per-request override was provided, and the request can still continue through Sapiom authorization/payment handling instead of being bypassed.

Logs, screenshots, and additional context

Relevant code:

  • packages/fetch/src/fetch.ts documents Request.__sapiom per-request overrides, including { enabled: false }.
  • packages/fetch/src/fetch.ts clones the input with new Request(input, init) before reading (request as any).__sapiom.
  • packages/fetch/src/interceptors.ts also reads metadata from the cloned request.

I checked open issue/PR searches for __sapiom / enabled and did not find an open duplicate.

Acknowledgements

  • I searched existing open and closed issues for this problem.
  • This report does not describe a suspected security vulnerability; I will use the Security Policy for private reporting.
  • I removed secrets, credentials, personal data, and other sensitive information from this report.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingneeds-triageAwaiting maintainer review and classification

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions