sentry-javascript-bundler-plugins icon indicating copy to clipboard operation
sentry-javascript-bundler-plugins copied to clipboard

Third-party Sentry releases leak into projects without specific releases

Open rene-leanix opened this issue 3 months ago • 7 comments

Environment

We are using @sentry/[email protected] in our web-app.

Expected Result

Only the releases we define in our use of the SDK or which we create in Sentry via the API or the GitHub action should be assigned to our projects.

Actual Result

We have observed that a large number of third-party release were created for our Sentry fall-back project, for which we deliberately never set or create a release. As it turned out, these releases are injected via the global SENTRY_RELEASE or __SENTRY_RELEASE__ variables that your bundler plugins create for third-party scripts, such as browser extensions, and are then applied to our project during the run-time of the SDK.

Here is one example: The release 1.25.0-b91f378 assigned to our fallback-project comes from the lace browser extension. The version is generated here and added as a Sentry version by your Webpack plugin. If a user now visits our web app with this plugin being loaded, our SDK code will pick up the release from the global variable set by the plugin on the shared window object.

Workaround

Our current workaround is to explicitly clear the release field in our code before it is sent to your servers.

Long-term solution

I would expect the code that the plugin generates to be associated with the Sentry project it is injected for to be able to only assign this version at run-time if the project matches.

rene-leanix avatar Aug 13 '25 09:08 rene-leanix

Hi @rene-leanix, thanks for looking into this and filing it. We discussed this a bit internally and haven't landed on a good solution yet. For now, please continue using your work-around of removing the releases field.

andreiborza avatar Aug 14 '25 13:08 andreiborza

We so far only have two ideas and both are not ideal:

  • We add an SDK option, something like readGlobalValues: boolean, which defaults to true but you could opt out of it to avoid the situation you described. My main pain point with that is that it puts the burden on the majority of users but doesn't fix it properly.
  • We somehow find a key that both the bundler plugins at build time and the SDK at runtime can agree on and we map the release values to these keys instead of just putting them onto the global directly. The problem is that there is no obvious key to do this automatically. For example, the DSN only contains the projectId while at build time we only know project and org slugs. Not even using the SDK version as a key is trivial because due to the timing of when we inject the release code snippet.

So we're definitely open to other suggestions. Also gonna tag @timfish - any ideas?

UPDATE: Actually, we could do something similar to moduleMetadataIntegration and the moduleMetadata plugin: When we inject the release code into the JS code at renderChunk, we know the filename. Also, if we throw a new Error() at SDK init time, we should get the file name. So we could use that filename as the key 🤔 This is just a bit problematic for webpack where we don't use the renderChunk approach but we inject the import to the virtual release file.

Lms24 avatar Aug 14 '25 13:08 Lms24

For example, the DSN only contains the projectId while at build time we only know project and org slugs.

Here's another hacky solution: could your bundle plugins scan the code for Sentry DSN strings and add the project-IDS collected from this as metadata to the global variable containing the release?? (You would probably use a new variable for this, e.g. __SCOPED_SENTRY_RELEASE__, and used this one rather than __SENTRY_RELEASE__ if it's available.)

rene-leanix avatar Aug 15 '25 09:08 rene-leanix

We could make it so that if you set release: undefined on init, then we don't read from the globals?

This would be a breaking change though since users might be relying on the current behaviour.

timfish avatar Aug 15 '25 10:08 timfish

We could make it so that if you set release: undefined on init, then we don't read from the globals?

This would be a breaking change though since users might be relying on the current behaviour.

What about release: null?

Either way, this comes very close to the work-around. Would be really good to achieve some kind of scoping for those global variables.

Another thought: what if your bundle plugins stopped creating global variables and rather replaced a specific placeholder in the code with release version?

rene-leanix avatar Aug 21 '25 15:08 rene-leanix

We could make it so that if you set release: undefined on init, then we don't read from the globals?

Not a fan of making a difference between not setting a value and setting it to undefined tbh.

What about release: null?

I guess this would be a bit better 😅 Though I'm fairly sure that users with a lenient/non-existing type checking setup set null with the same objective of not setting it at all.

As already stated, the main issue with this is that you as a "conventional" app developer have to set this to avoid being spammed with releases from browser extensions. Ideally, there's a world where this can be fixed by at its root.

Another thought: what if your bundle plugins stopped creating global variables and rather replaced a specific placeholder in the code with release version?

We could think about this yes. It needs to be implemented is a backwards-compatible way though. Meaning, the SDK must take the release from 1. Sentry.init, 2. the replaced placeholder if set, 3. from the global object if set, 4. otherwise not at all.

We'll need to discuss this a bit more internally what to do here.

Lms24 avatar Aug 25 '25 10:08 Lms24