Skip to content

Node SDK cloud mode accepts empty projectId, produces 404 URLs #15

Description

@AndresL230

Node SDK cloud mode accepts empty projectId, produces 404 URLs

Severity: Critical
Affected repos: middleware-node
Component boundary: middleware-node → API

Symptom

When init({ apiKey: "rc-..." }) is called without a projectId, the SDK silently enters cloud mode with an empty projectId. The transport then POSTs to https://api.recost.dev/projects//telemetry (two slashes). The API returns 404 on every flush. Telemetry is dropped silently.

Evidence

  • middleware-node/src/core/transport.tsresolveConfig() defaults projectId to "" rather than failing fast.
  • middleware-node/src/core/aggregator.ts — the aggregator's _projectId default is "" and is included in every WindowSummary.flush(). Even in local mode (no apiKey), this means every frame carries projectId: "", leaving the would-be extension server with no way to demultiplex.
  • middleware-node/src/init.tsinit() does not validate that projectId is non-empty when apiKey is set.

The same defect exists in the Python SDK and should be fixed symmetrically.

Impact

  • User installs the SDK, sets apiKey, forgets projectId. SDK appears to work — no errors, no warnings. Dashboard shows no data. User assumes the API is broken.
  • Misattribution risk: if the API ever accepts /projects//telemetry with an empty path segment (it currently 404s), data lands somewhere unintended.

Fix recommendation

Validate at init():

if (config.apiKey && (!config.projectId || config.projectId.trim() === "")) {
  throw new Error(
    "ReCost: projectId is required when apiKey is set (cloud mode). " +
    "Get a project ID from your dashboard at https://recost.dev/dashboard/projects"
  );
}

In local mode (no apiKey), projectId should still be required if multiple SDK instances might share the same extension server — see critical/01 for the multi-tenant binding requirement.

Mirror the same check in the Python SDK's _init.py so both SDKs behave identically.

Verification

  • Unit test: call init({ apiKey: "rc-test" }) with no projectId, assert it throws.
  • Unit test: call init({ apiKey: "rc-test", projectId: "" }) with empty string, assert it throws.
  • Unit test: call init({}) (local mode) without projectId, assert it does NOT throw (or document the local-mode policy).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions