Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

### Fixes

- Don't crash the iOS app when Sentry is initialized with an invalid DSN ([#6825](https://github.com/getsentry/sentry-react-native/pull/6825))
- Populate the Hermes runtime version on JS profiles instead of sending an empty value ([#6817](https://github.com/getsentry/sentry-react-native/pull/6817))

## 8.29.0
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.sentry.react

import androidx.test.core.app.ApplicationProvider
import com.facebook.react.bridge.JavaOnlyMap
import com.facebook.react.bridge.Promise
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
class RNSentryModuleInitInvalidDsnTest {
// Parity with the iOS regression test: an invalid DSN must be rejected at the
// bridge, not crash the host app. The Android SDK throws while parsing the DSN
// during init; initNativeSdk catches it and rejects the promise.
@Test
fun `initNativeSdk rejects on an invalid DSN instead of crashing`() {
val reactContext = Utils.makeReactContextMock()
whenever(reactContext.applicationContext).thenReturn(ApplicationProvider.getApplicationContext())
val module = RNSentryModuleImpl(reactContext)

val rnOptions = JavaOnlyMap.of("dsn", "\$SOME_ENV_VAR")
val promise = mock<Promise>()

// Must not throw out of the bridge (that would crash the app).
module.initNativeSdk(rnOptions, promise)

verify(promise).reject(eq("SentryReactNative"), anyOrNull<String>(), any<Throwable>())
verify(promise, never()).resolve(anyOrNull())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,31 @@
};
objectVersion = 56;
objects = {

/* Begin PBXBuildFile section */
00C7702ED388EA0682DBCC48 /* RNSentryStartInvalidOptionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26C9B2501FF3EBD8B91BA6C1 /* RNSentryStartInvalidOptionsTests.swift */; };
2639D71D3BD04F17B0BAC987 /* RNSentryTurboModulePerfControllerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = E795057A6D534A80A9D06356 /* RNSentryTurboModulePerfControllerTests.mm */; };
332D33472CDBDBB600547D76 /* RNSentryReplayOptionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 332D33462CDBDBB600547D76 /* RNSentryReplayOptionsTests.swift */; };
3339C4812D6625570088EB3A /* RNSentryUserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3339C4802D6625570088EB3A /* RNSentryUserTests.m */; };
3339C4832D6625570088EB3A /* RNSentryReplayControlTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3339C4822D6625570088EB3A /* RNSentryReplayControlTests.m */; };
336084392C32E382008CC412 /* RNSentryReplayBreadcrumbConverterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 336084382C32E382008CC412 /* RNSentryReplayBreadcrumbConverterTests.swift */; };
3380C6C42CE25ECA0018B9B6 /* RNSentryReplayPostInitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3380C6C32CE25ECA0018B9B6 /* RNSentryReplayPostInitTests.swift */; };
33AFDFED2B8D14B300AAB120 /* RNSentryFramesTrackerListenerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33AFDFEC2B8D14B300AAB120 /* RNSentryFramesTrackerListenerTests.m */; };

Check warning on line 17 in packages/core/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj

View check run for this annotation

@sentry/warden / warden: code-review

RNSentrySDK invalid-options test never hits the nil-options early return

A missing options path still builds empty fallback options and starts the SDK, so this assertion does not cover the new post-fallback nil guard; force both option creation paths to fail (or assert configureOptions is skipped only in that case) instead.
33AFDFF12B8D15E500AAB120 /* RNSentryDependencyContainerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33AFDFF02B8D15E500AAB120 /* RNSentryDependencyContainerTests.m */; };
33DEDFEA2D8DBE67006066E4 /* RNSentryOnDrawReporterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33DEDFE92D8DBE5B006066E4 /* RNSentryOnDrawReporterTests.swift */; };
33DEDFED2D8DC825006066E4 /* RNSentryOnDrawReporter+Test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 33DEDFEC2D8DC820006066E4 /* RNSentryOnDrawReporter+Test.mm */; };
33DEDFF02D9185EB006066E4 /* RNSentryTimeToDisplayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33DEDFEF2D9185E3006066E4 /* RNSentryTimeToDisplayTests.swift */; };
33F58AD02977037D008F60EA /* RNSentryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33F58ACF2977037D008F60EA /* RNSentryTests.m */; };
33F58AD12977037D008F60EB /* RNSentryScreenshotSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33F58ACE2977037D008F60EB /* RNSentryScreenshotSourceTests.swift */; };
2639D71D3BD04F17B0BAC987 /* RNSentryTurboModulePerfControllerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = E795057A6D534A80A9D06356 /* RNSentryTurboModulePerfControllerTests.mm */; };
AC4EE11A6E697E75C3753C16 /* RNSentrySDKStartInvalidOptionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 864F5BFD9CFDA4502ADAE72E /* RNSentrySDKStartInvalidOptionsTests.swift */; };
AEFB00422CC90C4B00EC8A9A /* RNSentryBreadcrumbTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3360843C2C340C76008CC412 /* RNSentryBreadcrumbTests.swift */; };
B4DEB41739F14AA38202D4D4 /* RNSentryUriValidationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E3742693F7643C2ADE1BDF2 /* RNSentryUriValidationTests.m */; };
B5859A50A3E865EF5E61465A /* libPods-RNSentryCocoaTesterTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 650CB718ACFBD05609BF2126 /* libPods-RNSentryCocoaTesterTests.a */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
1482D5685A340AB93348A43D /* Pods-RNSentryCocoaTesterTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNSentryCocoaTesterTests.release.xcconfig"; path = "Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests.release.xcconfig"; sourceTree = "<group>"; };
26C9B2501FF3EBD8B91BA6C1 /* RNSentryStartInvalidOptionsTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RNSentryStartInvalidOptionsTests.swift; sourceTree = "<group>"; };
330F308D2C0F385A002A0D4E /* RNSentryBreadcrumb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSentryBreadcrumb.h; path = ../ios/RNSentryBreadcrumb.h; sourceTree = "<group>"; };
332D33462CDBDBB600547D76 /* RNSentryReplayOptionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNSentryReplayOptionsTests.swift; sourceTree = "<group>"; };
332D33482CDBDC7300547D76 /* RNSentry.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNSentry.h; path = ../ios/RNSentry.h; sourceTree = SOURCE_ROOT; };
Expand All @@ -52,12 +55,13 @@
33DEDFEC2D8DC820006066E4 /* RNSentryOnDrawReporter+Test.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "RNSentryOnDrawReporter+Test.mm"; sourceTree = "<group>"; };
33DEDFEE2D8DD431006066E4 /* RNSentryTimeToDisplay.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNSentryTimeToDisplay.h; path = ../ios/RNSentryTimeToDisplay.h; sourceTree = SOURCE_ROOT; };
33DEDFEF2D9185E3006066E4 /* RNSentryTimeToDisplayTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNSentryTimeToDisplayTests.swift; sourceTree = "<group>"; };
33F58ACF2977037D008F60EA /* RNSentryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSentryTests.m; sourceTree = "<group>"; };
33F58ACE2977037D008F60EB /* RNSentryScreenshotSourceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNSentryScreenshotSourceTests.swift; sourceTree = "<group>"; };
33F58ACF2977037D008F60EA /* RNSentryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSentryTests.m; sourceTree = "<group>"; };
3E3742693F7643C2ADE1BDF2 /* RNSentryUriValidationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNSentryUriValidationTests.m; sourceTree = "<group>"; };
650CB718ACFBD05609BF2126 /* libPods-RNSentryCocoaTesterTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RNSentryCocoaTesterTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
E795057A6D534A80A9D06356 /* RNSentryTurboModulePerfControllerTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RNSentryTurboModulePerfControllerTests.mm; sourceTree = "<group>"; };
864F5BFD9CFDA4502ADAE72E /* RNSentrySDKStartInvalidOptionsTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RNSentrySDKStartInvalidOptionsTests.swift; sourceTree = "<group>"; };
E2321E7CFA55AB617247098E /* Pods-RNSentryCocoaTesterTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNSentryCocoaTesterTests.debug.xcconfig"; path = "Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests.debug.xcconfig"; sourceTree = "<group>"; };
E795057A6D534A80A9D06356 /* RNSentryTurboModulePerfControllerTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RNSentryTurboModulePerfControllerTests.mm; sourceTree = "<group>"; };
F48F26542EA2A481008A185E /* RNSentryEmitNewFrameEvent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNSentryEmitNewFrameEvent.h; path = ../ios/RNSentryEmitNewFrameEvent.h; sourceTree = SOURCE_ROOT; };
F48F26552EA2A4D4008A185E /* RNSentryFramesTrackerListener.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNSentryFramesTrackerListener.h; path = ../ios/RNSentryFramesTrackerListener.h; sourceTree = SOURCE_ROOT; };
FADF868E2EBD053E00D6652D /* SentrySDKWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentrySDKWrapper.h; path = ../ios/SentrySDKWrapper.h; sourceTree = SOURCE_ROOT; };
Expand Down Expand Up @@ -125,6 +129,8 @@
3360843C2C340C76008CC412 /* RNSentryBreadcrumbTests.swift */,
332D33462CDBDBB600547D76 /* RNSentryReplayOptionsTests.swift */,
3380C6C32CE25ECA0018B9B6 /* RNSentryReplayPostInitTests.swift */,
26C9B2501FF3EBD8B91BA6C1 /* RNSentryStartInvalidOptionsTests.swift */,
864F5BFD9CFDA4502ADAE72E /* RNSentrySDKStartInvalidOptionsTests.swift */,
);
path = RNSentryCocoaTesterTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -281,6 +287,8 @@
33DEDFF02D9185EB006066E4 /* RNSentryTimeToDisplayTests.swift in Sources */,
3380C6C42CE25ECA0018B9B6 /* RNSentryReplayPostInitTests.swift in Sources */,
33AFDFED2B8D14B300AAB120 /* RNSentryFramesTrackerListenerTests.m in Sources */,
00C7702ED388EA0682DBCC48 /* RNSentryStartInvalidOptionsTests.swift in Sources */,
AC4EE11A6E697E75C3753C16 /* RNSentrySDKStartInvalidOptionsTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
@_spi(Private) import Sentry
import XCTest

final class RNSentrySDKStartInvalidOptionsTests: XCTestCase {

// When neither the options file nor the fallback can produce options,
// the native auto-init path must not start the SDK with nil options.
func testStartWithUnparseableOptionsFileDoesNotStartSdk() {
// Arrange
SentrySDK.close()

// Act โ€” a non-existent options path forces the nil-options fallback
RNSentrySDK.start("/does/not/exist/sentry.options.json", configureOptions: nil)

// Assert
XCTAssertFalse(SentrySDK.isEnabled, "the native SDK must not start when options cannot be created")
}

Check warning on line 17 in packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentrySDKStartInvalidOptionsTests.swift

View check run for this annotation

@sentry/warden / warden: code-review

[RLL-HS2] RNSentrySDK invalid-options test never hits the nil-options early return (additional location)

A missing options path still builds empty fallback options and starts the SDK, so this assertion does not cover the new post-fallback nil guard; force both option creation paths to fail (or assert configureOptions is skipped only in that case) instead.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@_spi(Private) import Sentry
import XCTest

final class RNSentryStartInvalidOptionsTests: XCTestCase {

// An invalid DSN must surface a parse error, not start the native SDK with nil
// options (which crashes it).
func testStartWithInvalidDsnSurfacesErrorAndDoesNotStartSdk() {
// Arrange
SentrySDK.close()

var error: NSError?

// Act โ€” mirrors an unexpanded build variable reaching the DSN
RNSentryStart.start(options: [
"dsn": "$SOME_ENV_VAR"

Check warning on line 16 in packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryStartInvalidOptionsTests.swift

View check run for this annotation

@sentry/warden / warden: code-review

[RLL-HS2] RNSentrySDK invalid-options test never hits the nil-options early return (additional location)

A missing options path still builds empty fallback options and starts the SDK, so this assertion does not cover the new post-fallback nil guard; force both option creation paths to fail (or assert configureOptions is skipped only in that case) instead.
], error: &error)

// Assert
XCTAssertNotNil(error, "an invalid DSN must surface a parse error instead of starting the SDK with nil options")
XCTAssertFalse(SentrySDK.isEnabled, "the native SDK must not start when option parsing fails")
}
}
7 changes: 7 additions & 0 deletions packages/core/ios/RNSentrySDK.m
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ + (void)start:(NSString *)path configureOptions:(void (^)(SentryOptions *options
}
}

if (options == nil) {
// Even the fallback failed. Skip starting instead of passing nil options to
// the native SDK, which would crash it.
NSLog(@"[RNSentry] Could not create options; skipping native SDK start.");
return;
}

[RNSentryStart updateWithReactDefaults:options];
if (configureOptions != nil) {
configureOptions(options);
Expand Down
5 changes: 5 additions & 0 deletions packages/core/ios/RNSentryStart.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ + (void)startWithOptions:(NSDictionary *_Nonnull)javascriptOptions
{
SentryOptions *options = [self createOptionsWithDictionary:javascriptOptions
error:errorPointer];
if (options == nil) {
// Parsing failed (e.g. invalid DSN) and set `*errorPointer`. Return early so
// the caller can reject; starting with nil options crashes the native SDK.
return;
}
[self updateWithReactDefaults:options];
[self updateWithReactFinals:options];
NSString *jsSdkVersion = nil;
Expand Down
Loading