Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
59318ce
feat: add opt-in SPM integration for TalsecRuntime on iOS
martinzigrai Jul 14, 2026
f6c7a58
ci: add non-blocking iOS SPM smoke-test job
martinzigrai Jul 14, 2026
8ee8c1f
feat: add TalsecRuntime SPM embed helper and placeholder wiring
martinzigrai Jul 16, 2026
c4e1532
feat: scaffold opt-in Expo iOS SPM support
martinzigrai Jul 16, 2026
5bc2690
feat: make SPM the sole iOS delivery for TalsecRuntime (stage 2 prep)
martinzigrai Jul 16, 2026
6d804b5
feat: enable Expo iOS SPM support by default
martinzigrai Jul 16, 2026
383d91a
ci: build iOS example via SPM only
martinzigrai Jul 16, 2026
80ed393
refactor: default to SPM with vendored fallback (react-native-firebas…
martinzigrai Jul 16, 2026
e30a6b6
fix: guard Expo iOS SPM embed on SPM-active condition
martinzigrai Jul 16, 2026
2e502d5
ci: add vendored fallback gate alongside non-blocking SPM build
martinzigrai Jul 16, 2026
16543ee
feat: point iOS SPM at Free-RASP-ReactNative-SPM manifest repo (exact…
martinzigrai Jul 16, 2026
f49f4f7
fix: make SPM embed helper idempotent to avoid duplicate framework embed
martinzigrai Jul 16, 2026
245a667
fix: resolve Expo plugin eslint errors (no-shadow, avoid namespace im…
martinzigrai Jul 16, 2026
9868751
feat: keep package.swift in the project
tompsota Jul 20, 2026
2994e85
fix: harden iOS SPM integration
tompsota Jul 20, 2026
3d9fdfc
fix: ci + changelog
tompsota Jul 21, 2026
09d58a4
refactor: consolidate iOS SPM environment flag
tompsota Jul 21, 2026
a86538e
docs: streamline SPM integration guidance
tompsota Jul 21, 2026
5ec6a90
release: freeRASP 5.1.0
tompsota Jul 21, 2026
6e0f2d9
fix(android): return reusable event identifiers
tompsota Jul 21, 2026
bfb6438
fix: changelog
tompsota Jul 21, 2026
900b20d
feat: raise ios sdk version
tompsota Jul 21, 2026
b92b69d
fix: changelog
tompsota Jul 21, 2026
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
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ jobs:
run: |
yarn expo:typecheck

- name: Test Expo plugin
run: yarn test:plugin

- name: Format check
run: yarn prettier --check

Expand Down Expand Up @@ -84,6 +87,8 @@ jobs:
build-ios:
runs-on: macos-latest
needs: build-library
env:
FREERASP_USE_SPM: "0"
steps:
- name: Checkout
uses: actions/checkout@v6
Expand All @@ -99,3 +104,25 @@ jobs:
working-directory: ./example
run: |
yarn build:ios

build-ios-spm:
runs-on: macos-latest
needs: build-library
env:
USE_FRAMEWORKS: dynamic
FREERASP_USE_SPM: "1"
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Setup
uses: ./.github/actions/setup

- name: Install pods (SPM)
working-directory: ./example/ios
run: pod install

- name: Build example for iOS (SPM)
working-directory: ./example
run: |
yarn build:ios
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ xcuserdata
*.xccheckout
*.moved-aside
DerivedData
.build/
*.hmap
*.ipa
*.xcuserstate
Expand Down
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,34 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [5.1.0]

- Android SDK version: 18.3.0
- iOS SDK version: 7.1.0

### React Native

#### Fixed

- Fixed identifier retrieval after React Native context reinitialization ([#152](https://github.com/talsec/Free-RASP-ReactNative/issues/152)).

#### Added

- Added Swift Package Manager delivery of `TalsecRuntime`, enabled by default for Expo projects and available as an opt-in for bare React Native 0.75 or newer projects.
- Added the Expo config plugin option `ios.useSpm`; set it to `false` to use the vendored XCFramework fallback.

### iOS

#### Added

- Added support for postponed checks, therefore, due to slower execution, some subchecks are run after initial startup checks.
- Improved hook detection.

#### Fixed

- Fixed issue with app's color scheme initialization.
- Fixed bad memory access in jaibreak check.

## [5.0.0] - 2026-05-15

- Android SDK version: 18.3.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,24 +108,28 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex

// Trigger lazy initialization of the freeRASP events
private fun initializeEventKeys() {
ThreatEvent.ALL_EVENTS
RaspExecutionStateEvent.ALL_EVENTS
ThreatEvent.ALL_EVENT_IDENTIFIERS
RaspExecutionStateEvent.ALL_EVENT_IDENTIFIERS
}

/**
* Method to get the random identifiers of callbacks
*/
@ReactMethod
fun getThreatIdentifiers(promise: Promise) {
promise.resolve(ThreatEvent.ALL_EVENTS)
promise.resolve(
Arguments.fromList(ThreatEvent.ALL_EVENT_IDENTIFIERS)
)
}

/**
* Method to get the random identifiers of callbacks
*/
@ReactMethod
fun getRaspExecutionStateIdentifiers(promise: Promise) {
promise.resolve(RaspExecutionStateEvent.ALL_EVENTS)
promise.resolve(
Arguments.fromList(RaspExecutionStateEvent.ALL_EVENT_IDENTIFIERS)
)
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.freeraspreactnative.events

import com.facebook.react.bridge.Arguments
import com.freeraspreactnative.utils.RandomGenerator

internal sealed class RaspExecutionStateEvent(override val value: Int) : BaseRaspEvent {
Expand All @@ -12,9 +11,8 @@ internal sealed class RaspExecutionStateEvent(override val value: Int) : BaseRas
companion object Companion {
internal val CHANNEL_NAME = RandomGenerator.next().toString()
internal val CHANNEL_KEY = RandomGenerator.next().toString()
internal val ALL_EVENTS = Arguments.fromList(
listOf(
AllChecksFinished
).map { it.value })
internal val ALL_EVENT_IDENTIFIERS = listOf(
AllChecksFinished
).map { it.value }
}
}
48 changes: 23 additions & 25 deletions android/src/main/java/com/freeraspreactnative/events/ThreatEvent.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.freeraspreactnative.events

import com.facebook.react.bridge.Arguments
import com.freeraspreactnative.utils.RandomGenerator

/**
Expand Down Expand Up @@ -41,29 +40,28 @@ internal sealed class ThreatEvent(override val value: Int) : BaseRaspEvent {
internal val CHANNEL_KEY = RandomGenerator.next().toString()
internal val MALWARE_CHANNEL_KEY = RandomGenerator.next().toString()

internal val ALL_EVENTS = Arguments.fromList(
listOf(
AppIntegrity,
PrivilegedAccess,
Debug,
Hooks,
Passcode,
Simulator,
SecureHardwareNotAvailable,
SystemVPN,
DeviceBinding,
UnofficialStore,
ObfuscationIssues,
DevMode,
Malware,
ADBEnabled,
Screenshot,
ScreenRecording,
MultiInstance,
TimeSpoofing,
LocationSpoofing,
UnsecureWifi,
Automation
).map { it.value })
internal val ALL_EVENT_IDENTIFIERS = listOf(
AppIntegrity,
PrivilegedAccess,
Debug,
Hooks,
Passcode,
Simulator,
SecureHardwareNotAvailable,
SystemVPN,
DeviceBinding,
UnofficialStore,
ObfuscationIssues,
DevMode,
Malware,
ADBEnabled,
Screenshot,
ScreenRecording,
MultiInstance,
TimeSpoofing,
LocationSpoofing,
UnsecureWifi,
Automation
).map { it.value }
}
}
23 changes: 23 additions & 0 deletions example/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@ require Pod::Executable.execute_command('node', ['-p',
platform :ios, min_ios_version_supported
prepare_react_native_project!

# SPM CI sets FREERASP_USE_SPM=1 and requires dynamic frameworks.
use_freerasp_spm = ENV['FREERASP_USE_SPM'] == '1'
linkage = ENV['USE_FRAMEWORKS']
if use_freerasp_spm
if linkage != nil && linkage != 'dynamic'
raise Pod::Informative, 'freeRASP SPM requires USE_FRAMEWORKS=dynamic'
end
linkage = 'dynamic'
end
if linkage != nil
Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
use_frameworks! :linkage => linkage.to_sym
Expand All @@ -31,5 +39,20 @@ target 'FreeraspRNExample' do
:mac_catalyst_enabled => false,
# :ccache_enabled => true
)

# React Native 0.78 pins fmt 11.0.2, whose C++20 consteval path does not
# compile with Xcode 26.4+. Keep only fmt on C++17 until RN is upgraded to
# 0.83 or newer, which includes the upstream fmt fix.
installer.pods_project.targets.each do |target|
next unless target.name == 'fmt'

target.build_configurations.each do |build_configuration|
build_configuration.build_settings['CLANG_CXX_LANGUAGE_STANDARD'] = 'c++17'
end
end

# Embed TalsecRuntime when SPM is enabled.
require_relative '../../freerasp_spm.rb'
freerasp_embed_talsec_spm!(installer)
end
end
39 changes: 31 additions & 8 deletions freerasp-react-native.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ require "json"
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'

talsec_spm_path = File.expand_path('ios/TalsecRuntimePackage', __dir__)

Pod::Spec.new do |s|
s.name = "freerasp-react-native"
s.version = package["version"]
Expand All @@ -14,14 +16,35 @@ Pod::Spec.new do |s|
s.platforms = { :ios => "11.0" }
s.source = { :git => "https://github.com/talsec/freerasp-react-native.git", :tag => "#{s.version}" }

s.source_files = 'ios/models/*.{h,m,mm,swift}',
'ios/utils/*.{h,m,mm,swift}',
'ios/dispatchers/*.{h,m,mm,swift}',
'ios/*.{h,m,mm,swift}',
'ios/TalsecRuntime.xcframework'
s.xcconfig = { 'OTHER_LDFLAGS' => '-framework TalsecRuntime' }
s.ios.vendored_frameworks = "ios/TalsecRuntime.xcframework"

spm_requested = ENV['FREERASP_USE_SPM'] == '1'
use_spm = spm_requested && respond_to?(:spm_dependency, true)

if spm_requested && !respond_to?(:spm_dependency, true)
Pod::UI.warn '[freeRASP][SPM] React Native does not provide spm_dependency; using the vendored TalsecRuntime framework.'
end

source_globs = [
'ios/models/*.{h,m,mm,swift}',
'ios/utils/*.{h,m,mm,swift}',
'ios/dispatchers/*.{h,m,mm,swift}',
'ios/*.{h,m,mm,swift}',
]

if use_spm
s.ios.deployment_target = '13.0'
spm_dependency(s,
url: talsec_spm_path,
requirement: {},
products: ['TalsecRuntime']
)
else
source_globs << 'ios/TalsecRuntime.xcframework'
s.xcconfig = { 'OTHER_LDFLAGS' => '-framework TalsecRuntime' }
s.ios.vendored_frameworks = 'ios/TalsecRuntime.xcframework'
end

s.source_files = source_globs

# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
# See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
if respond_to?(:install_modules_dependencies, true)
Expand Down
88 changes: 88 additions & 0 deletions freerasp_spm.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Links and embeds the local TalsecRuntime Swift package.

def freerasp_embed_talsec_spm!(installer,
package_path: File.expand_path('ios/TalsecRuntimePackage', __dir__),
product: 'TalsecRuntime')

package_path = File.expand_path(package_path)
local_pkg_class = Xcodeproj::Project::Object::XCLocalSwiftPackageReference
remote_pkg_class = Xcodeproj::Project::Object::XCRemoteSwiftPackageReference
ref_class = Xcodeproj::Project::Object::XCSwiftPackageProductDependency

spm_active = ENV['FREERASP_USE_SPM'] == '1' &&
respond_to?(:spm_dependency, true)

if spm_active && !File.file?(File.join(package_path, 'Package.swift'))
raise Pod::Informative, "[freeRASP][SPM] Package.swift not found at #{package_path}"
end

projects_and_targets = {}
pods_project = installer.pods_project
projects_and_targets[pods_project] = pods_project.targets.select do |target|
target.name == 'freerasp-react-native'
end

installer.aggregate_targets.each do |aggregate_target|
project = aggregate_target.user_project
next if project.nil?

app_targets = aggregate_target.user_targets.select do |t|
t.respond_to?(:product_type) && t.product_type == 'com.apple.product-type.application'
end
projects_and_targets[project] ||= []
projects_and_targets[project].concat(app_targets)
end

new_object = lambda do |project, klass|
uuid = project.generate_uuid
uuid = project.generate_uuid while project.objects_by_uuid.key?(uuid)
object = klass.new(project, uuid)
object.initialize_defaults
object
end

projects_and_targets.each do |project, targets|
targets.uniq!

# Remove existing references before recreating them.
owned_packages = []
targets.each do |target|
target.package_product_dependencies.select do |reference|
reference.class == ref_class && reference.product_name == product
end.each do |reference|
owned_packages << reference.package unless reference.package.nil?
reference.remove_from_project
end
end
project.root_object.package_references.each do |package|
reference_path =
if package.class == local_pkg_class
package.relative_path
elsif package.class == remote_pkg_class
package.repositoryURL
end
next if reference_path.nil?

owned_packages << package if File.basename(reference_path) == 'TalsecRuntimePackage'
end
owned_packages.uniq.each do |package|
package.remove_from_project
end

if spm_active
pkg = new_object.call(project, local_pkg_class)
pkg.relative_path = package_path
project.root_object.package_references << pkg

targets.each do |target|
Pod::UI.puts "[freeRASP][SPM] Linking #{product} to target #{target.name}"
dep = new_object.call(project, ref_class)
dep.package = pkg
dep.product_name = product
target.package_product_dependencies << dep
end
end

project.save
end
end
Loading