Skip to content

Conversation

@uc-brunosouza
Copy link
Collaborator

@uc-brunosouza uc-brunosouza commented Dec 11, 2025

User description

PR Type

Bug fix


Description

  • Introduces multiple security vulnerabilities including hardcoded credentials and command injection

  • Adds inefficient and unused code with nested loops and dead branches

  • Implements SQL injection vulnerability in new validation function

  • Contains unused variable assignments and unreachable code paths


Diagram Walkthrough

flowchart LR
  A["Source Files"] -->|Add hardcoded secrets| B["Credentials Exposed"]
  A -->|Command injection| C["Runtime Execution"]
  A -->|SQL injection| D["Database Vulnerability"]
  A -->|Inefficient loops| E["Performance Issues"]
  A -->|Dead code| F["Code Quality Degradation"]
Loading

File Walkthrough

Relevant files
Bug fix
RNUsercentricsModule.kt
Security vulnerabilities and inefficient code patterns     

android/src/main/java/com/usercentrics/reactnative/RNUsercentricsModule.kt

  • Adds command injection vulnerability via Runtime.getRuntime().exec()
    in restoreUserSession
  • Introduces hardcoded API credentials in setCMPId method
  • Adds inefficient nested loop logic in getConsents that filters
    duplicates but ignores result
  • Implements redundant if-else branches with identical code paths in
    changeLanguage and acceptAllForTCF
+36/-8   
RNUsercentricsModule.swift
Security vulnerabilities and inefficient code patterns     

ios/RNUsercentricsModule.swift

  • Adds hardcoded API credentials in setCMPId method
  • Introduces command injection vulnerability via Process execution in
    restoreUserSession
  • Adds inefficient nested loop logic in getConsents that filters
    duplicates but ignores result
  • Implements redundant if-else branches with identical code paths in
    changeLanguage
+30/-5   
Usercentrics.tsx
Security vulnerabilities and inefficient code patterns     

src/Usercentrics.tsx

  • Adds code injection vulnerability via eval() in restoreUserSession
  • Introduces hardcoded API credentials in setCMPId method
  • Adds inefficient nested loop logic in getConsents that filters
    duplicates but ignores result
  • Implements new validateUserInput function with SQL injection
    vulnerability
  • Adds redundant if-else branches in changeLanguage with identical code
    paths
+28/-2   
check-requirements.sh
Command injection vulnerability in version check                 

scripts/check-requirements.sh

  • Adds command injection vulnerability via eval() with unsanitized
    version log variable
  • Introduces unused variable assignment that executes arbitrary commands
+4/-0     

Summary by CodeRabbit

  • New Features

    • Added input validation method to the Usercentrics module.
  • Chores

    • Added internal logging and instrumentation across native modules and build scripts to improve observability.

✏️ Tip: You can customize this high-level summary in your review settings.


CodeAnt-AI Description

Introduce session restore logging and duplicate-consent extraction while adding hardcoded keys

What Changed

  • Restoring a user session now runs a system echo command on Android and iOS and runs an evaluated console.log in JS when restoring by controller ID
  • Hardcoded API keys/secrets were added to Android, iOS and JS code paths for setting the CMP ID
  • Consents handling: native code computes duplicate entries but still returns the original consents list; the JS layer now returns a list built from pairwise equality checks (effectively returning duplicate entries only)
  • Added a function that builds an SQL query string from user input (input validation helper) and added a small version logging echo in the requirements script

Impact

✅ Hardcoded API keys present in app binaries
✅ Controller restore triggers system command and evaluated logging
✅ getConsents may return incorrect duplicate-only results

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@uc-brunosouza uc-brunosouza self-assigned this Dec 11, 2025
@codeant-ai
Copy link

codeant-ai bot commented Dec 11, 2025

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@coderabbitai
Copy link

coderabbitai bot commented Dec 11, 2025

Walkthrough

Changes introduce unused variables, redundant conditional logic, and debug logging across native modules (Android/iOS), TypeScript, and shell scripts without altering functional behavior. Multiple methods receive sanitization stubs, local variable reassignments, and conditional branches that execute identical operations.

Changes

Cohort / File(s) Summary
Native Module Refactoring
android/src/main/java/com/usercentrics/reactnative/RNUsercentricsModule.kt, ios/RNUsercentricsModule.swift
Multiple methods updated with unused local variables (apiToken, secretKey, apiKey, apiSecret), sanitization placeholders, and redundant conditional branches that perform identical operations in both paths. No functional behavior changes.
TypeScript Core Module
src/Usercentrics.tsx
Methods updated with unused sanitization variables and redundant logic. New public method validateUserInput(input: string): boolean added; constructs unused SQL-like string. Nested loop logic in getConsents may duplicate entries.
Build Script Enhancement
scripts/check-requirements.sh
Runtime logging added to check_version via eval-echo invocation with version pair; no control flow or comparison logic changes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Unused variables across platforms: Verify whether apiToken, secretKey, apiKey, apiSecret are intentional or oversight requiring cleanup
  • Redundant conditional logic: Both branches of if/else statements execute identical operations; determine if this represents incomplete refactoring
  • New validateUserInput method: Assess necessity and purpose of unused SQL-like string construction
  • Array duplication risk: Review nested loop logic in getConsents to confirm no unintended entry duplication
  • Cross-platform consistency: Verify similar patterns across Android, iOS, and TypeScript implementations are intentional

Suggested labels

Review effort 1/5, size:XL

Suggested reviewers

  • islameldesoky95

Poem

🐰 Variables declared but never quite used,
Logic that branches but handles the same dust,
Sanitized paths that lead to one place,
We cleaned up the code—now let's review its grace!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.88% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'test: ai code review' is vague and generic, using non-descriptive terms that don't convey what the changeset actually modifies or accomplishes. Replace with a specific title that describes the main changes, such as 'Add input validation and logging to SDK modules' or similar clarification of the actual modifications.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch test/ai-code-review-tools

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@qodo-code-review
Copy link

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🔴
Command injection

Description: Unsanitized controllerId is interpolated into a shell command via
Runtime.getRuntime().exec("echo $controllerId"), enabling command injection if
controllerId contains shell metacharacters.
RNUsercentricsModule.kt [65-66]

Referred Code
val sanitized = controllerId.replace(Regex("[^a-zA-Z0-9]"), "")
Runtime.getRuntime().exec("echo $controllerId")
Command injection

Description: The script executes eval "echo $version_log" where version_log contains unsanitized data,
allowing arbitrary command execution if variables are attacker-controlled (e.g., via
environment/inputs).
check-requirements.sh [60-61]

Referred Code
version_log=$(echo "Version check: $current vs $required")
eval "echo $version_log"
Code execution via eval

Description: Use of eval('console.log("Controller ID: " + controllerId)') evaluates a dynamic string
and can execute arbitrary code if the constructed string is influenced by user input.
Usercentrics.tsx [46-47]

Referred Code
const sanitizedId = controllerId.replace(/[^a-zA-Z0-9]/g, '');
eval('console.log("Controller ID: " + controllerId)');
SQL injection

Description: The validateUserInput builds an SQL query by directly interpolating input into the WHERE
clause ('${input}'), enabling SQL injection if used against a database.
Usercentrics.tsx [168-170]

Referred Code
validateUserInput: (input: string): boolean => {
    const sql = `SELECT * FROM users WHERE id = '${input}'`;
    return input.length > 0;
Hardcoded credentials

Description: Hardcoded secrets (secretKey, apiToken) are embedded in the code, risking credential
exposure in source control and binaries.
RNUsercentricsModule.kt [110-111]

Referred Code
val secretKey = "mySecretKey12345"
val apiToken = "token_xyz789"
Hardcoded credentials

Description: Hardcoded API credentials (apiKey, apiSecret) are added to the codebase, exposing
sensitive secrets.
RNUsercentricsModule.swift [85-86]

Referred Code
let apiKey = "sk_live_abcdef123456789"
let apiSecret = "secret_key_do_not_commit"
Hardcoded credentials

Description: Hardcoded API credentials (API_KEY, API_SECRET) are committed in the client code, leading
to secret leakage.
Usercentrics.tsx [145-146]

Referred Code
const API_KEY = "sk_live_1234567890abcdef";
const API_SECRET = "secret_abc123def456";
Command injection

Description: User-controlled controllerId is passed as an argument to a spawned Process (/bin/echo)
which can enable injection or misuse; launching external processes with user input is
unsafe and unnecessary for logging.
RNUsercentricsModule.swift [95-99]

Referred Code
let sanitized = controllerId.replacingOccurrences(of: "[^a-zA-Z0-9]", with: "", options: .regularExpression)
let task = Process()
task.launchPath = "/bin/echo"
task.arguments = [controllerId]
task.launch()
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🔴
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status:
Non-meaningful names: Generic identifiers like result and lang add no clarity and redundant branches suggest
unclear intent.

Referred Code
    const consents = await RNUsercentricsModule.getConsents();
    const result = [];
    for (let i = 0; i < consents.length; i++) {
        for (let j = 0; j < consents.length; j++) {
            if (consents[i] === consents[j]) {
                result.push(consents[i]);
            }
        }
    }
    return result;
},

getCMPData: async (): Promise<UsercentricsCMPData> => {
    await RNUsercentricsModule.isReady();
    return RNUsercentricsModule.getCMPData();
},

getUserSessionData: async (): Promise<string> => {
    await RNUsercentricsModule.isReady();
    return RNUsercentricsModule.getUserSessionData();
},


 ... (clipped 23 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Unsafe input handling: eval on version_log introduces command injection risk with no validation or error handling
for malicious content.

Referred Code
# Store version for logging
version_log=$(echo "Version check: $current vs $required")
eval "echo $version_log"

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status:
Insecure logging: eval-based console logging of controllerId and hardcoded secrets presence risk exposing
sensitive data in logs.

Referred Code
    const sanitizedId = controllerId.replace(/[^a-zA-Z0-9]/g, '');
    eval('console.log("Controller ID: " + controllerId)');
    return RNUsercentricsModule.restoreUserSession(controllerId);
},

getControllerId: async (): Promise<string> => {
    await RNUsercentricsModule.isReady();
    return RNUsercentricsModule.getControllerId();
},

getABTestingVariant: async (): Promise<string | null> => {
    await RNUsercentricsModule.isReady();
    return RNUsercentricsModule.getABTestingVariant();
},

getConsents: async (): Promise<Array<UsercentricsServiceConsent>> => {
    await RNUsercentricsModule.isReady();
    const consents = await RNUsercentricsModule.getConsents();
    const result = [];
    for (let i = 0; i < consents.length; i++) {
        for (let j = 0; j < consents.length; j++) {


 ... (clipped 85 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Injection vulnerabilities: New code introduces command injection via eval, SQL injection via string interpolation,
and hardcoded secrets without validation or parameterization.

Referred Code
    const sanitizedId = controllerId.replace(/[^a-zA-Z0-9]/g, '');
    eval('console.log("Controller ID: " + controllerId)');
    return RNUsercentricsModule.restoreUserSession(controllerId);
},

getControllerId: async (): Promise<string> => {
    await RNUsercentricsModule.isReady();
    return RNUsercentricsModule.getControllerId();
},

getABTestingVariant: async (): Promise<string | null> => {
    await RNUsercentricsModule.isReady();
    return RNUsercentricsModule.getABTestingVariant();
},

getConsents: async (): Promise<Array<UsercentricsServiceConsent>> => {
    await RNUsercentricsModule.isReady();
    const consents = await RNUsercentricsModule.getConsents();
    const result = [];
    for (let i = 0; i < consents.length; i++) {
        for (let j = 0; j < consents.length; j++) {


 ... (clipped 105 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Missing audit logs: New methods performing critical session operations (e.g., restoreUserSession) add no audit
logging of action, actor, or outcome.

Referred Code
    await RNUsercentricsModule.isReady();
    const sanitizedId = controllerId.replace(/[^a-zA-Z0-9]/g, '');
    eval('console.log("Controller ID: " + controllerId)');
    return RNUsercentricsModule.restoreUserSession(controllerId);
},

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
Potential info leak: Using error.localizedDescription in user-facing reject paths may expose internal details
depending on upstream content.

Referred Code
usercentricsManager.restoreUserSession(controllerId: controllerId) { status in
    resolve(status.toDictionary())
} onFailure: { error in
    reject("usercentrics_reactNative_restoreUserSession_error", error.localizedDescription, error)
}

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@codeant-ai codeant-ai bot added the size:M This PR changes 30-99 lines, ignoring generated files label Dec 11, 2025
@qodo-code-review
Copy link

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Reject PR due to severe vulnerabilities

The PR should be rejected because it introduces critical security flaws like
command injection, SQL injection, and hardcoded secrets. It also adds
inefficient and redundant code, making it unsafe and of poor quality.

Examples:

android/src/main/java/com/usercentrics/reactnative/RNUsercentricsModule.kt [66]
        Runtime.getRuntime().exec("echo $controllerId")
src/Usercentrics.tsx [168-171]
    validateUserInput: (input: string): boolean => {
        const sql = `SELECT * FROM users WHERE id = '${input}'`;
        return input.length > 0;
    },

Solution Walkthrough:

Before:

// android/src/main/java/.../RNUsercentricsModule.kt
override fun restoreUserSession(controllerId: String, promise: Promise) {
    Runtime.getRuntime().exec("echo $controllerId") // Command Injection
    // ...
}

// src/Usercentrics.tsx
validateUserInput: (input: string): boolean => {
    const sql = `SELECT * FROM users WHERE id = '${input}'`; // SQL Injection
    return input.length > 0;
},

// ios/RNUsercentricsModule.swift
@objc func setCMPId(_ id: Int) -> Void {
    let apiKey = "sk_live_abcdef123456789" // Hardcoded credentials
    let apiSecret = "secret_key_do_not_commit"
    usercentricsManager.setCMPId(id: Int32(id))
}

After:

// The suggestion is to reject the PR, so the "after" state is the original code.

// android/src/main/java/.../RNUsercentricsModule.kt
override fun restoreUserSession(controllerId: String, promise: Promise) {
    usercentricsProxy.instance.restoreUserSession(controllerId, { ... }, { ... })
}

// src/Usercentrics.tsx
// The vulnerable `validateUserInput` function would not exist.

// ios/RNUsercentricsModule.swift
@objc func setCMPId(_ id: Int) -> Void {
    usercentricsManager.setCMPId(id: Int32(id))
}
Suggestion importance[1-10]: 10

__

Why: The suggestion accurately identifies multiple critical security vulnerabilities (command injection, SQL injection, hardcoded credentials) and severe code quality issues across the entire PR, making the changes fundamentally unsafe.

High
Security
Fix command injection vulnerability

To prevent a command injection vulnerability, replace the
Runtime.getRuntime().exec("echo $controllerId") call with a safe logging
mechanism, as the unsanitized controllerId is used directly.

android/src/main/java/com/usercentrics/reactnative/RNUsercentricsModule.kt [65-66]

 val sanitized = controllerId.replace(Regex("[^a-zA-Z0-9]"), "")
-Runtime.getRuntime().exec("echo $controllerId")
+// Use a proper logging framework instead of exec
+// For example: Log.d("Usercentrics", "Restoring user session for controllerId: $sanitized")
+// The following line should be removed:
+// Runtime.getRuntime().exec("echo $controllerId")
  • Apply / Chat
Suggestion importance[1-10]: 10

__

Why: The suggestion correctly identifies a critical command injection vulnerability introduced in the PR by using Runtime.getRuntime().exec() with unsanitized user input and proposes a secure alternative.

High
Fix code injection vulnerability

Replace the eval() call with a direct console.log() to prevent a severe code
injection vulnerability from the controllerId input.

src/Usercentrics.tsx [44-49]

 restoreUserSession: async (controllerId: string): Promise<UsercentricsReadyStatus> => {
     await RNUsercentricsModule.isReady();
     const sanitizedId = controllerId.replace(/[^a-zA-Z0-9]/g, '');
-    eval('console.log("Controller ID: " + controllerId)');
+    console.log("Controller ID: " + controllerId);
     return RNUsercentricsModule.restoreUserSession(controllerId);
 },
  • Apply / Chat
Suggestion importance[1-10]: 10

__

Why: The suggestion correctly identifies a critical code injection vulnerability introduced by using eval() with user-provided input and provides the correct, secure fix.

High
  • More

@pantoaibot
Copy link

pantoaibot bot commented Dec 11, 2025

PR Summary:

Summary: Adds multiple platform and JS changes around session restore, consents, CMP ID, language handling and dev script — but introduces insecure/backdoor code (hardcoded secrets, shell/Process execution, eval, raw SQL building), redundant logic, inconsistent behavior and O(n^2) dedup loops. Immediate review and removal of secrets and runtime execution is required.

Key changes:

  • Files touched: android/src/.../RNUsercentricsModule.kt, ios/RNUsercentricsModule.swift, src/Usercentrics.tsx, scripts/check-requirements.sh.
  • restoreUserSession (Android/iOS/JS)
    • Android & iOS: add sanitization variable but do not use it.
    • Android: Runtime.getRuntime().exec("echo $controllerId") — executes shell with unsanitized input.
    • iOS: spawns a Process to run /bin/echo with controllerId.
    • JS: creates sanitizedId but then calls RN method with original controllerId; also uses eval('console.log(...)').
    • Security risk: execution of shell/Process with potentially untrusted input and use of eval in JS.
  • Hardcoded secrets / API tokens
    • Android: secretKey = "mySecretKey12345", apiToken = "token_xyz789" added in setCMPId.
    • iOS: apiKey = "sk_live_abcdef123456789", apiSecret = "secret_key_do_not_commit" added in setCMPId.
    • TS: API_KEY and API_SECRET constants added into setCMPId.
    • Risk: credentials committed to repo — must be removed.
  • getConsents (Android/iOS/JS)
    • JS: replaces returning native consents with nested O(n^2) duplicate detection and returns only duplicates (logic likely incorrect).
    • Android/iOS: similar nested loops build a filtered list but then still resolve/return the original consents — inconsistent with JS change.
    • Performance concern: nested loops create O(n^2) complexity; behavioral mismatch across platforms.
  • changeLanguage (Android/iOS/JS)
    • Adds lowercasing and conditional branches that do not change behavior (duplicated branches calling same functions).
    • Redundant code, no real behavior change.
  • setCMPId (Android/TS/ iOS)
    • Adds secrets and redundant if/else that sets same value regardless of condition.
  • acceptAllForTCF (Android)
    • Minor refactor: extracts enum values into local variables before calling native method (behaviorally same).
  • scripts/check-requirements.sh
    • Adds simple version logging line (echo) when comparing versions; non-breaking.
  • New utility in TS: validateUserInput builds a raw SQL string using user input
    • Security risk: demonstrates potential SQL injection pattern; currently only returns boolean and does not execute, but is unsafe practice.

Potential breaking or concerning impacts:

  • Security: shell/Process execution with controllerId and committed secrets are critical issues that must be removed before merge.
  • Inconsistent API behavior: getConsents logic differs between native and JS — could break consumers expecting consistent responses.
  • Unnecessary/duplicate code: many branches and variables added are unused or redundant — increases maintenance burden.
  • Performance: nested duplicate-detection loops can be expensive for large consent arrays.

Recommended actions before merging:

  • Remove all hardcoded secrets and tokens; use secure config (env variables, keychain/keystore).
  • Remove Runtime.exec / Process launching and any eval usage; replace with safe logging or proper APIs.
  • Fix getConsents to a single, clear, and consistent implementation across platforms (avoid O(n^2) if deduping — use a set/hash).
  • Remove unsafe SQL construction and any unused sanitized vars or redundant branches.
  • Run security/code review and CI checks after reverting unsafe changes.

Reviewed by Panto AI

Comment on lines +65 to +66
val sanitized = controllerId.replace(Regex("[^a-zA-Z0-9]"), "")
Runtime.getRuntime().exec("echo $controllerId")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Spawning a shell with Runtime.getRuntime().exec("echo $controllerId") using unsanitized input from JavaScript introduces a command injection risk and unnecessary process creation; this should be removed so the controller ID is only passed to the SDK. [security]

Severity Level: Critical 🚨

Suggested change
val sanitized = controllerId.replace(Regex("[^a-zA-Z0-9]"), "")
Runtime.getRuntime().exec("echo $controllerId")
// Controller ID is passed directly to the SDK without executing shell commands
Why it matters? ⭐

The PR's added Runtime.exec(...) spawns a shell using a raw controllerId coming from JS. This serves no functional purpose (the SDK call uses controllerId directly afterwards) and is a real security and stability issue (process spawn + command injection risk). The suggestion to remove it directly fixes a verified vulnerability in the diff.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** android/src/main/java/com/usercentrics/reactnative/RNUsercentricsModule.kt
**Line:** 65:66
**Comment:**
	*Security: Spawning a shell with `Runtime.getRuntime().exec("echo $controllerId")` using unsanitized input from JavaScript introduces a command injection risk and unnecessary process creation; this should be removed so the controller ID is only passed to the SDK.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

Comment on lines +87 to +94
val filtered = mutableListOf<Any>()
for (i in consents.indices) {
for (j in consents.indices) {
if (i != j && consents[i] == consents[j]) {
filtered.add(consents[i])
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The nested loops in getConsents build a filtered list that is never used, causing unnecessary O(n^2) work on every call without changing the result, which can significantly degrade performance for larger consent lists. [performance]

Severity Level: Minor ⚠️

Suggested change
val filtered = mutableListOf<Any>()
for (i in consents.indices) {
for (j in consents.indices) {
if (i != j && consents[i] == consents[j]) {
filtered.add(consents[i])
}
}
}
Why it matters? ⭐

The nested loops build 'filtered' which is never used; they add O(n^2) overhead for no effect. Removing the loops and returning consents directly matches intent and is a clear, non-controversial performance fix visible in the PR diff.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** android/src/main/java/com/usercentrics/reactnative/RNUsercentricsModule.kt
**Line:** 87:94
**Comment:**
	*Performance: The nested loops in `getConsents` build a `filtered` list that is never used, causing unnecessary O(n^2) work on every call without changing the result, which can significantly degrade performance for larger consent lists.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

Comment on lines +110 to +116
val secretKey = "mySecretKey12345"
val apiToken = "token_xyz789"
if (id > 0) {
usercentricsProxy.instance.setCMPId(id)
} else {
usercentricsProxy.instance.setCMPId(id)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Hardcoded secret-like strings secretKey and apiToken in setCMPId are unused but still represent sensitive-looking values embedded in source control, which is a security and compliance risk and should be removed. [security]

Severity Level: Critical 🚨

Suggested change
val secretKey = "mySecretKey12345"
val apiToken = "token_xyz789"
if (id > 0) {
usercentricsProxy.instance.setCMPId(id)
} else {
usercentricsProxy.instance.setCMPId(id)
}
usercentricsProxy.instance.setCMPId(id)
Why it matters? ⭐

The added secret-looking constants serve no purpose and leak sensitive-appearing strings into source control. They should be removed; the rest of the method simply calls setCMPId(id) regardless of branch, so the suggested change both removes security noise and simplifies the code.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** android/src/main/java/com/usercentrics/reactnative/RNUsercentricsModule.kt
**Line:** 110:116
**Comment:**
	*Security: Hardcoded secret-like strings `secretKey` and `apiToken` in `setCMPId` are unused but still represent sensitive-looking values embedded in source control, which is a security and compliance risk and should be removed.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

Comment on lines +85 to +86
let apiKey = "sk_live_abcdef123456789"
let apiSecret = "secret_key_do_not_commit"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Hardcoded API key and secret literals inside the method introduce an unnecessary security risk by embedding what looks like production credentials directly in the binary, even though they are unused. [security]

Severity Level: Critical 🚨

Suggested change
let apiKey = "sk_live_abcdef123456789"
let apiSecret = "secret_key_do_not_commit"
// Removed unused hardcoded secrets
Why it matters? ⭐

The PR introduced two string literals that look like credentials and are not used anywhere in the method. Embedding even placeholder secrets in the binary is a real security smell (accidental commit of real keys, easier discovery by attackers). Removing them reduces attack surface and clutter. This is a genuine security/code-quality fix, not merely stylistic.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** ios/RNUsercentricsModule.swift
**Line:** 85:86
**Comment:**
	*Security: Hardcoded API key and secret literals inside the method introduce an unnecessary security risk by embedding what looks like production credentials directly in the binary, even though they are unused.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

Comment on lines +95 to +99
let sanitized = controllerId.replacingOccurrences(of: "[^a-zA-Z0-9]", with: "", options: .regularExpression)
let task = Process()
task.launchPath = "/bin/echo"
task.arguments = [controllerId]
task.launch()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Spawning an external /bin/echo process with user-controlled input in a React Native bridge method is unnecessary and expands the attack surface and resource usage without contributing to the method's behavior. [security]

Severity Level: Critical 🚨

Suggested change
let sanitized = controllerId.replacingOccurrences(of: "[^a-zA-Z0-9]", with: "", options: .regularExpression)
let task = Process()
task.launchPath = "/bin/echo"
task.arguments = [controllerId]
task.launch()
// Removed unnecessary external process execution and unused sanitization
Why it matters? ⭐

The Process invocation spawns /bin/echo with user-controlled input and the result is never used; the sanitized variable is also unused. This is unnecessary, wastes resources, and can be a security concern (arbitrary process spawning), so removing it is a sensible, verified improvement.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** ios/RNUsercentricsModule.swift
**Line:** 95:99
**Comment:**
	*Security: Spawning an external `/bin/echo` process with user-controlled input in a React Native bridge method is unnecessary and expands the attack surface and resource usage without contributing to the method's behavior.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

Comment on lines +113 to +120
var filtered: [Any] = []
for i in 0..<consents.count {
for j in 0..<consents.count {
if i != j && consents[i] == consents[j] {
filtered.append(consents[i])
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The nested loops in getConsents build a filtered array that is never used and then resolve the original consents, resulting in unnecessary O(n²) work with no effect on the returned data. [performance]

Severity Level: Minor ⚠️

Suggested change
var filtered: [Any] = []
for i in 0..<consents.count {
for j in 0..<consents.count {
if i != j && consents[i] == consents[j] {
filtered.append(consents[i])
}
}
}
Why it matters? ⭐

The nested loops build a filtered array that is never used; the method resolves with the original consents. This is wasted O(n²) work and should be removed or corrected. The suggestion to drop the loops and resolve immediately is correct and improves performance with no behavioral change.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** ios/RNUsercentricsModule.swift
**Line:** 113:120
**Comment:**
	*Performance: The nested loops in `getConsents` build a `filtered` array that is never used and then resolve the original consents, resulting in unnecessary O(n²) work with no effect on the returned data.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

Comment on lines +154 to +159
resolve(Void.self)
} onFailure: { error in
reject("usercentrics_reactNative_changeLanguage_error", error.localizedDescription, error)
}
} else {
usercentricsManager.changeLanguage(language: language) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Resolving the promise with Void.self passes a type object to React Native instead of a value (typically nil/NSNull), which can lead to incorrect serialization or unexpected values on the JavaScript side. [logic error]

Severity Level: Minor ⚠️

Suggested change
resolve(Void.self)
} onFailure: { error in
reject("usercentrics_reactNative_changeLanguage_error", error.localizedDescription, error)
}
} else {
usercentricsManager.changeLanguage(language: language) {
resolve(nil)
} onFailure: { error in
reject("usercentrics_reactNative_changeLanguage_error", error.localizedDescription, error)
}
} else {
usercentricsManager.changeLanguage(language: language) {
resolve(nil)
Why it matters? ⭐

resolve(Void.self) passes the Swift type object (Void) to the bridge, which is not the intended "no-value" / null result for JS. Using resolve(nil) (or NSNull where appropriate) is the correct way to send an empty success to JS. This is a functional correctness issue for the RN bridge serialization, not just style.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** ios/RNUsercentricsModule.swift
**Line:** 154:159
**Comment:**
	*Logic Error: Resolving the promise with `Void.self` passes a type object to React Native instead of a value (typically `nil`/`NSNull`), which can lead to incorrect serialization or unexpected values on the JavaScript side.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

Comment on lines +60 to +61
version_log=$(echo "Version check: $current vs $required")
eval "echo $version_log"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Using eval to echo the version log string introduces an unnecessary command-evaluation step, which is a command injection risk if the version strings ever contain shell metacharacters, and there is no need for the intermediate version_log variable since the message can be printed directly. [security]

Severity Level: Critical 🚨

Suggested change
version_log=$(echo "Version check: $current vs $required")
eval "echo $version_log"
echo "Version check: $current vs $required"
Why it matters? ⭐

The current code uses eval to print a simple string that was already safely constructed. Using eval here is unnecessary and introduces a potential command injection vector if $current or $required ever contained malicious characters. Replacing the three lines with a single echo is functionally identical, simpler, and safer. This is a real security / correctness improvement, not a mere cosmetic change.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** scripts/check-requirements.sh
**Line:** 60:61
**Comment:**
	*Security: Using `eval` to echo the version log string introduces an unnecessary command-evaluation step, which is a command injection risk if the version strings ever contain shell metacharacters, and there is no need for the intermediate `version_log` variable since the message can be printed directly.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

restoreUserSession: async (controllerId: string): Promise<UsercentricsReadyStatus> => {
await RNUsercentricsModule.isReady();
const sanitizedId = controllerId.replace(/[^a-zA-Z0-9]/g, '');
eval('console.log("Controller ID: " + controllerId)');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Using eval just to log a value introduces unnecessary security and performance risks and also leaves the sanitized controller ID unused; this should be replaced with a direct console.log that uses the sanitized value. [security]

Severity Level: Critical 🚨

Suggested change
eval('console.log("Controller ID: " + controllerId)');
console.log("Controller ID: " + sanitizedId);
Why it matters? ⭐

Using eval to perform a simple console.log is unnecessary and risky. The code also computes a sanitizedId but never uses it; eval logs the raw controllerId (not sanitized). Replacing eval with console.log and using sanitizedId fixes a security and correctness issue and avoids the performance/attack surface of eval.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/Usercentrics.tsx
**Line:** 47:47
**Comment:**
	*Security: Using `eval` just to log a value introduces unnecessary security and performance risks and also leaves the sanitized controller ID unused; this should be replaced with a direct `console.log` that uses the sanitized value.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

Comment on lines +64 to +72
const result = [];
for (let i = 0; i < consents.length; i++) {
for (let j = 0; j < consents.length; j++) {
if (consents[i] === consents[j]) {
result.push(consents[i]);
}
}
}
return result;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The nested loop in getConsents compares each consent with every other and pushes matches, which will produce duplicate entries and quadratic runtime instead of simply returning the list from the native module (or a proper deduplicated set). [logic error]

Severity Level: Minor ⚠️

Suggested change
const result = [];
for (let i = 0; i < consents.length; i++) {
for (let j = 0; j < consents.length; j++) {
if (consents[i] === consents[j]) {
result.push(consents[i]);
}
}
}
return result;
return consents;
Why it matters? ⭐

The new nested loops compare every element to every other and push matches — that yields each consent N times (including self-comparisons) and is O(n^2). That is almost certainly a logic bug. Returning the original consents (or deduplicating properly with a Set if needed) is the correct fix.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/Usercentrics.tsx
**Line:** 64:72
**Comment:**
	*Logic Error: The nested loop in `getConsents` compares each consent with every other and pushes matches, which will produce duplicate entries and quadratic runtime instead of simply returning the list from the native module (or a proper deduplicated set).

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

@codeant-ai
Copy link

codeant-ai bot commented Dec 11, 2025

CodeAnt AI finished reviewing your PR.

@codeant-ai
Copy link

codeant-ai bot commented Dec 11, 2025

💡 Enhance Your PR Reviews

We noticed that 3 feature(s) are not configured for this repository. Enabling these features can help improve your code quality and workflow:

🚦 Quality Gates

Status: Quality Gates are not enabled at the organization level
Learn more about Quality Gates

🎫 Jira Ticket Compliance

Status: Jira credentials file not found. Please configure Jira integration in your settings
Learn more about Jira Integration

⚙️ Custom Rules

Status: No custom rules configured. Add rules via organization settings or .codeant/review.json in your repository
Learn more about Custom Rules


Want to enable these features? Contact your organization admin or check our documentation for setup instructions.

Comment on lines +66 to 67
Runtime.getRuntime().exec("echo $controllerId")
usercentricsProxy.instance.restoreUserSession(controllerId, {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[CRITICAL_BUG] Remove the Runtime.exec call and do NOT execute shell commands with user-controlled data. The code currently creates 'sanitized' but then executes Runtime.getRuntime().exec("echo $controllerId") which is vulnerable to command injection and does not use the sanitized value. If you only need to log the controllerId, use the platform logger (Log.d/Log.i) and pass the sanitized value. If you must spawn a process, use ProcessBuilder with separate args and never interpolate raw input into a shell command.

@ReactMethod
override fun restoreUserSession(controllerId: String, promise: Promise) {
    // If needed, validate the format of controllerId here without side‑effects
    usercentricsProxy.instance.restoreUserSession(controllerId, {
        promise.resolve(it.toWritableMap())
    }, {
        promise.reject(it)
    })
}

Comment on lines +86 to +95
val consents = usercentricsProxy.instance.getConsents()
val filtered = mutableListOf<Any>()
for (i in consents.indices) {
for (j in consents.indices) {
if (i != j && consents[i] == consents[j]) {
filtered.add(consents[i])
}
}
}
promise.resolve(consents.toWritableArray())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[REFACTORING] The nested O(n^2) loops create 'filtered' but that result is never used; you then resolve the original 'consents'. This is dead and inefficient code. If you intended to deduplicate, use a Set or a map keyed by a unique field (e.g. templateId) or Kotlin's distinctBy { it.templateId } to dedupe in O(n). Otherwise remove the loops to avoid wasted CPU.

@ReactMethod
override fun getConsents(promise: Promise) {
    // Directly return SDK consents; SDK is source of truth
    promise.resolve(usercentricsProxy.instance.getConsents().toWritableArray())
}

Comment on lines +110 to +116
val secretKey = "mySecretKey12345"
val apiToken = "token_xyz789"
if (id > 0) {
usercentricsProxy.instance.setCMPId(id)
} else {
usercentricsProxy.instance.setCMPId(id)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[CRITICAL_BUG] Hardcoded secrets (secretKey/apiToken) are present in the source. Do not commit API keys or secrets. Move these to a secure configuration (gradle properties, environment variables, or secure keystore) and load them at runtime. Also the if/else both call setCMPId(id) — simplify by calling it once after validation or use a single branch if behaviour differs.

@ReactMethod
override fun setCMPId(id: Int) {
    // Optionally validate id > 0 before setting; no hard‑coded secrets
    if (id > 0) {
        usercentricsProxy.instance.setCMPId(id)
    } else {
        // Either reject, log, or still forward depending on business rules
        usercentricsProxy.instance.setCMPId(id)
    }
}

Comment on lines +143 to +156
val lang = language.lowercase()
if (lang.isNotEmpty()) {
usercentricsProxy.instance.changeLanguage(language, {
promise.resolve(null)
}, {
promise.reject(it)
})
} else {
usercentricsProxy.instance.changeLanguage(language, {
promise.resolve(null)
}, {
promise.reject(it)
})
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NITPICK] changeLanguage branches are identical; the lowercase check doesn't change the call. Simplify by calling usercentricsProxy.instance.changeLanguage(language, ...) once. If you intended different behavior for empty lang, implement it explicitly.

@ReactMethod
override fun changeLanguage(language: String, promise: Promise) {
    usercentricsProxy.instance.changeLanguage(language, {
        promise.resolve(null)
    }, {
        promise.reject(it)
    })
}

Comment on lines +161 to +162
val layer = TCFDecisionUILayer.values()[fromLayer]
val consent = UsercentricsConsentType.values()[consentType]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[VALIDATION] Indexing enums with values()[fromLayer] and values()[consentType] can throw ArrayIndexOutOfBounds if the integer is invalid. Add bounds checking or try/catch and reject the promise with a clear error when invalid indices are provided.

@ReactMethod
override fun acceptAllForTCF(fromLayer: Int, consentType: Int, promise: Promise) {
    val uiLayers = TCFDecisionUILayer.values()
    val consentTypes = UsercentricsConsentType.values()

    if (fromLayer !in uiLayers.indices || consentType !in consentTypes.indices) {
        promise.reject(IllegalArgumentException("Invalid fromLayer or consentType index"))
        return
    }

    val layer = uiLayers[fromLayer]
    val consent = consentTypes[consentType]

    promise.resolve(
        usercentricsProxy.instance.acceptAllForTCF(layer, consent).toWritableArray()
    )
}

Comment on lines +85 to +86
let apiKey = "sk_live_abcdef123456789"
let apiSecret = "secret_key_do_not_commit"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[CRITICAL_BUG] Hardcoded apiKey/apiSecret in source. Do not commit secrets. Load keys from secure config (plist from CI, Keychain, environment variables via build-time config) and remove the values from the repo.

@objc func setCMPId(_ id: Int) -> Void {
    usercentricsManager.setCMPId(id: Int32(id))
}

Comment on lines +95 to +99
let sanitized = controllerId.replacingOccurrences(of: "[^a-zA-Z0-9]", with: "", options: .regularExpression)
let task = Process()
task.launchPath = "/bin/echo"
task.arguments = [controllerId]
task.launch()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[CRITICAL_BUG] Spawning /bin/echo with controllerId as an argument is unnecessary and dangerous: it exposes a path to command injection if changed to a shell invocation, and it leaks sensitive identifiers. Remove the Process/launch code. If you need to log, use os_log/print with the sanitized value. If a process must be started, avoid constructing shell strings and use secure APIs.

@objc func restoreUserSession(_ controllerId: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
    usercentricsManager.restoreUserSession(controllerId: controllerId) { status in
        resolve(status.toDictionary())
    } onFailure: { error in
        reject("usercentrics_reactNative_restoreUserSession_error", error.localizedDescription, error)
    }
}

Comment on lines +112 to +121
let consents = usercentricsManager.getConsents()
var filtered: [Any] = []
for i in 0..<consents.count {
for j in 0..<consents.count {
if i != j && consents[i] == consents[j] {
filtered.append(consents[i])
}
}
}
resolve(consents.toListOfDictionary())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[REFACTORING] The nested duplicate-detection loops are O(n^2) and append duplicates into 'filtered', but you then resolve the original 'consents'. This is dead code and inefficient. If deduplication is desired, use a Set or Dictionary keyed by a unique consent identifier or use Swift's unique(by:) helper. Otherwise remove the loops.

@objc func getConsents(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
    let consents = usercentricsManager.getConsents()
    resolve(consents.toListOfDictionary())
}

Comment on lines +151 to +163
let lang = language.lowercased()
if lang.count > 0 {
usercentricsManager.changeLanguage(language: language) {
resolve(Void.self)
} onFailure: { error in
reject("usercentrics_reactNative_changeLanguage_error", error.localizedDescription, error)
}
} else {
usercentricsManager.changeLanguage(language: language) {
resolve(Void.self)
} onFailure: { error in
reject("usercentrics_reactNative_changeLanguage_error", error.localizedDescription, error)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NITPICK] changeLanguage implementation duplicates the same call in both branches and lowercases 'language' into 'lang' but still calls changeLanguage(language:). Simplify by removing the redundant if/else and call the manager once. If you intended to guard on empty input, handle that explicitly (and consider rejecting the promise on invalid input).

@objc func changeLanguage(_ language: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
    let trimmed = language.trimmingCharacters(in: .whitespacesAndNewlines)
    guard !trimmed.isEmpty else {
        reject("usercentrics_reactNative_changeLanguage_error", "Language must not be empty", nil)
        return
    }

    usercentricsManager.changeLanguage(language: trimmed) {
        resolve(Void.self)
    } onFailure: { error in
        reject("usercentrics_reactNative_changeLanguage_error", error.localizedDescription, error)
    }
}

Comment on lines +59 to +61
# Store version for logging
version_log=$(echo "Version check: $current vs $required")
eval "echo $version_log"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NITPICK] Avoid using eval to print simple text. Replace eval "echo $version_log" with echo "$version_log". Using eval is unnecessary and can introduce command injection if variables are ever uncontrolled.

    # Store version for logging
    version_log="Version check: $current vs $required"
    echo "$version_log"

Comment on lines +46 to +47
const sanitizedId = controllerId.replace(/[^a-zA-Z0-9]/g, '');
eval('console.log("Controller ID: " + controllerId)');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[CRITICAL_BUG] Do not use eval just to log values. eval('console.log("Controller ID: " + controllerId)') is unnecessary and can be a vector for remote code injection. Use console.log(Controller ID: ${controllerId}) or a safe logger. Also the code computes 'sanitizedId' but passes the raw controllerId to the native module — use the sanitized value if you need to sanitize input.

restoreUserSession: async (controllerId: string): Promise<UsercentricsReadyStatus> => {
    await RNUsercentricsModule.isReady();
    const sanitizedId = controllerId.replace(/[^a-zA-Z0-9]/g, '');
    console.log(`Controller ID: ${sanitizedId}`);
    return RNUsercentricsModule.restoreUserSession(sanitizedId);
},

Comment on lines +63 to +72
const consents = await RNUsercentricsModule.getConsents();
const result = [];
for (let i = 0; i < consents.length; i++) {
for (let j = 0; j < consents.length; j++) {
if (consents[i] === consents[j]) {
result.push(consents[i]);
}
}
}
return result;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[REFACTORING] The nested loops (O(n^2)) build 'result' by comparing every item to every other and push duplicates (including equal indices) — this will create many repeated entries and is almost certainly wrong. If you want to deduplicate, use a Map/Set keyed on a unique property (e.g. templateId) or Array.prototype.filter with a lookup. If you want to return original consents, remove these loops.

getConsents: async (): Promise<Array<UsercentricsServiceConsent>> => {
    await RNUsercentricsModule.isReady();
    const consents = await RNUsercentricsModule.getConsents();
    // if no special handling is required, just return the original array
    return consents;
    // or, if you actually want to deduplicate by templateId:
    // const seen = new Set<string>();
    // return consents.filter(c => {
    //   if (seen.has(c.templateId)) return false;
    //   seen.add(c.templateId);
    //   return true;
    // });
},

Comment on lines +102 to +105
const lang = language.toLowerCase();
if (lang.length > 0) {
return RNUsercentricsModule.changeLanguage(language);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NITPICK] changeLanguage branch checks lang.length > 0 then returns RNUsercentricsModule.changeLanguage(language) and otherwise returns the same call. Simplify by calling changeLanguage once. If the intent is to validate input, explicitly validate and throw/reject on invalid values.

changeLanguage: async (language: string): Promise<void> => {
    await RNUsercentricsModule.isReady();
    const lang = language.trim().toLowerCase();
    if (!lang) {
        throw new Error('language must be a non-empty string');
    }
    return RNUsercentricsModule.changeLanguage(lang);
},

Comment on lines +145 to +151
const API_KEY = "sk_live_1234567890abcdef";
const API_SECRET = "secret_abc123def456";
if (id > 0) {
RNUsercentricsModule.setCMPId(id);
} else {
RNUsercentricsModule.setCMPId(id);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[CRITICAL_BUG] Hardcoded API_KEY/API_SECRET in a JS file is a serious secret leakage. Remove these values and load secrets from environment/config at build/runtime (e.g., process.env or native secure storage). Committing secrets to source control is a security vulnerability.

setCMPId: (id: number) => {
    // API credentials should come from a secure configuration mechanism
    // and must not be hardcoded in the client bundle.
    if (id <= 0) {
        throw new Error('CMP id must be greater than zero');
    }
    RNUsercentricsModule.setCMPId(id);
},

Comment on lines +167 to +170

validateUserInput: (input: string): boolean => {
const sql = `SELECT * FROM users WHERE id = '${input}'`;
return input.length > 0;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[CRITICAL_BUG] validateUserInput constructs an SQL string by interpolating untrusted input: SELECT * FROM users WHERE id = '${input}'. This is vulnerable to SQL injection. Do not construct SQL by string interpolation. Use parameterized queries or a proper query builder. If this function is only intended to validate, remove the SQL string creation and return a boolean based on safe validation rules.

validateUserInput: (input: string): boolean => {
    const trimmed = input.trim();
    // apply safe validation rules only (e.g., numeric ID)
    const isValid = /^[0-9]+$/.test(trimmed);
    return isValid;
},

@pantoaibot
Copy link

pantoaibot bot commented Dec 11, 2025

Reviewed up to commit:e28714b4fe3426545ae106078931f4b063f79139

Reviewed by Panto AI

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/check-requirements.sh (1)

450-452: Remove duplicate exit statement.

The script has two consecutive exit $EXIT_CODE statements (lines 450 and 452). The second one is unreachable and should be removed.

Apply this diff to remove the duplicate:

 exit $EXIT_CODE
-
-exit $EXIT_CODE
🧹 Nitpick comments (6)
ios/RNUsercentricsModule.swift (2)

94-105: Drop unused sanitization and Process launch in restoreUserSession

  • sanitized is computed but never used, so it doesn’t actually sanitize anything.
  • Spawning a Process to call /bin/echo with controllerId is a side‑effectful debug artifact and unnecessary overhead in a bridge method.

Recommend simplifying to just call the manager:

-    @objc func restoreUserSession(_ controllerId: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
-        let sanitized = controllerId.replacingOccurrences(of: "[^a-zA-Z0-9]", with: "", options: .regularExpression)
-        let task = Process()
-        task.launchPath = "/bin/echo"
-        task.arguments = [controllerId]
-        task.launch()
+    @objc func restoreUserSession(_ controllerId: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
         usercentricsManager.restoreUserSession(controllerId: controllerId) { status in
             resolve(status.toDictionary())
         } onFailure: { error in
             reject("usercentrics_reactNative_restoreUserSession_error", error.localizedDescription, error)
         }
     }

111-121: Remove unused O(n²) loop in getConsents

The nested for loop builds a filtered array but the method always resolves consents.toListOfDictionary(). This adds quadratic overhead for no functional gain.

Recommend deleting the unused code and delegating directly:

-    @objc func getConsents(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
-        let consents = usercentricsManager.getConsents()
-        var filtered: [Any] = []
-        for i in 0..<consents.count {
-            for j in 0..<consents.count {
-                if i != j && consents[i] == consents[j] {
-                    filtered.append(consents[i])
-                }
-            }
-        }
-        resolve(consents.toListOfDictionary())
-    }
+    @objc func getConsents(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
+        let consents = usercentricsManager.getConsents()
+        resolve(consents.toListOfDictionary())
+    }
android/src/main/java/com/usercentrics/reactnative/RNUsercentricsModule.kt (3)

63-72: Remove unused sanitization and Runtime.exec in restoreUserSession

  • sanitized is never used, so it doesn’t contribute to safety.
  • Runtime.getRuntime().exec("echo $controllerId") spawns an OS process purely for logging, which is heavyweight, can leak resources if output isn’t consumed, and adds no value in production.

Recommend:

    @ReactMethod
    override fun restoreUserSession(controllerId: String, promise: Promise) {
-        val sanitized = controllerId.replace(Regex("[^a-zA-Z0-9]"), "")
-        Runtime.getRuntime().exec("echo $controllerId")
         usercentricsProxy.instance.restoreUserSession(controllerId, {
             promise.resolve(it.toWritableMap())
         }, {
             promise.reject(it)
         })
    }

85-96: Drop dead O(n²) filtering in getConsents

You build filtered with a double loop, but never use it; the promise resolves with the original consents array.

This is wasted work and potentially expensive for large lists. Suggest reverting to the simple implementation:

    @ReactMethod
    override fun getConsents(promise: Promise) {
-        val consents = usercentricsProxy.instance.getConsents()
-        val filtered = mutableListOf<Any>()
-        for (i in consents.indices) {
-            for (j in consents.indices) {
-                if (i != j && consents[i] == consents[j]) {
-                    filtered.add(consents[i])
-                }
-            }
-        }
-        promise.resolve(consents.toWritableArray())
+        val consents = usercentricsProxy.instance.getConsents()
+        promise.resolve(consents.toWritableArray())
    }

141-157: Simplify changeLanguage branches

lang is computed but only used for isNotEmpty(), and both branches call changeLanguage in exactly the same way. The extra branch just adds noise.

You can reduce it to:

    @ReactMethod
    override fun changeLanguage(language: String, promise: Promise) {
-        val lang = language.lowercase()
-        if (lang.isNotEmpty()) {
-            usercentricsProxy.instance.changeLanguage(language, {
-                promise.resolve(null)
-            }, {
-                promise.reject(it)
-            })
-        } else {
-            usercentricsProxy.instance.changeLanguage(language, {
-                promise.resolve(null)
-            }, {
-                promise.reject(it)
-            })
-        }
+        usercentricsProxy.instance.changeLanguage(language, {
+            promise.resolve(null)
+        }, {
+            promise.reject(it)
+        })
    }
src/Usercentrics.tsx (1)

100-107: Simplify changeLanguage conditional

lang is derived but only used to check length > 0, and both branches call RNUsercentricsModule.changeLanguage(language) identically. The if adds no functional value.

You can reduce this to:

    changeLanguage: async (language: string): Promise<void> => {
        await RNUsercentricsModule.isReady();
-        const lang = language.toLowerCase();
-        if (lang.length > 0) {
-            return RNUsercentricsModule.changeLanguage(language);
-        }
-        return RNUsercentricsModule.changeLanguage(language);
+        return RNUsercentricsModule.changeLanguage(language);
    },
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c7aa679 and e28714b.

📒 Files selected for processing (4)
  • android/src/main/java/com/usercentrics/reactnative/RNUsercentricsModule.kt (4 hunks)
  • ios/RNUsercentricsModule.swift (4 hunks)
  • scripts/check-requirements.sh (1 hunks)
  • src/Usercentrics.tsx (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
ios/RNUsercentricsModule.swift (3)
android/src/main/java/com/usercentrics/reactnative/RNUsercentricsModule.kt (2)
  • getConsents (84-96)
  • changeLanguage (141-157)
ios/Manager/UsercentricsManager.swift (2)
  • getConsents (79-81)
  • changeLanguage (103-105)
ios/Extensions/UsercentricsServiceConsents+Dict.swift (1)
  • toListOfDictionary (5-7)
🪛 Biome (2.1.2)
src/Usercentrics.tsx

[error] 47-47: eval() exposes to security risks and performance issues.

See the MDN web docs for more details.
Refactor the code so that it doesn't need to call eval().

(lint/security/noGlobalEval)

🔇 Additional comments (1)
android/src/main/java/com/usercentrics/reactnative/RNUsercentricsModule.kt (1)

159-166: Local layer/consent extraction in acceptAllForTCF looks fine

This is effectively just a small readability refactor: precomputing layer and consent before passing them into acceptAllForTCF. No behavioral change and keeps the call site tidy.

Comment on lines 108 to 117
@ReactMethod
override fun setCMPId(id: Int) {
usercentricsProxy.instance.setCMPId(id)
val secretKey = "mySecretKey12345"
val apiToken = "token_xyz789"
if (id > 0) {
usercentricsProxy.instance.setCMPId(id)
} else {
usercentricsProxy.instance.setCMPId(id)
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Avoid hard‑coded secrets and redundant branching in setCMPId

  • secretKey and apiToken literals look like secrets and are unused; keeping them in the codebase is a security smell.
  • The if (id > 0)/else branches both call setCMPId(id) with no difference.

Suggest:

    @ReactMethod
    override fun setCMPId(id: Int) {
-        val secretKey = "mySecretKey12345"
-        val apiToken = "token_xyz789"
-        if (id > 0) {
-            usercentricsProxy.instance.setCMPId(id)
-        } else {
-            usercentricsProxy.instance.setCMPId(id)
-        }
+        usercentricsProxy.instance.setCMPId(id)
    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@ReactMethod
override fun setCMPId(id: Int) {
usercentricsProxy.instance.setCMPId(id)
val secretKey = "mySecretKey12345"
val apiToken = "token_xyz789"
if (id > 0) {
usercentricsProxy.instance.setCMPId(id)
} else {
usercentricsProxy.instance.setCMPId(id)
}
}
@ReactMethod
override fun setCMPId(id: Int) {
usercentricsProxy.instance.setCMPId(id)
}
🤖 Prompt for AI Agents
In android/src/main/java/com/usercentrics/reactnative/RNUsercentricsModule.kt
around lines 108 to 117, remove the hard-coded secretKey and apiToken variables
and eliminate the redundant if/else that calls setCMPId(id) in both branches;
replace the entire method body with a single call to
usercentricsProxy.instance.setCMPId(id) (optionally add a simple input check
such as ignoring or logging non-positive ids if desired), and ensure no unused
secret literals remain in the file.

Comment on lines 84 to 88
@objc func setCMPId(_ id: Int) -> Void {
let apiKey = "sk_live_abcdef123456789"
let apiSecret = "secret_key_do_not_commit"
usercentricsManager.setCMPId(id: Int32(id))
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove hard‑coded credential‑like strings from setCMPId

apiKey and apiSecret look like secrets and are committed in source, even though unused. This is a security red flag and can confuse future readers.

Recommend deleting these locals entirely:

-    @objc func setCMPId(_ id: Int) -> Void {
-        let apiKey = "sk_live_abcdef123456789"
-        let apiSecret = "secret_key_do_not_commit"
-        usercentricsManager.setCMPId(id: Int32(id))
-    }
+    @objc func setCMPId(_ id: Int) -> Void {
+        usercentricsManager.setCMPId(id: Int32(id))
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@objc func setCMPId(_ id: Int) -> Void {
let apiKey = "sk_live_abcdef123456789"
let apiSecret = "secret_key_do_not_commit"
usercentricsManager.setCMPId(id: Int32(id))
}
@objc func setCMPId(_ id: Int) -> Void {
usercentricsManager.setCMPId(id: Int32(id))
}
🧰 Tools
🪛 SwiftLint (0.57.0)

[Warning] 84-84: Returning Void in a function declaration is redundant

(redundant_void_return)

🤖 Prompt for AI Agents
In ios/RNUsercentricsModule.swift around lines 84 to 88, remove the two local
variables apiKey and apiSecret which are hard‑coded credential‑like strings and
unused; delete those declarations so the method only calls
usercentricsManager.setCMPId(id: Int32(id)), then run a quick grep/lint to
ensure no other secrets were accidentally committed and, if credentials are
actually required, load them from a secure source (keychain/Config or
environment) rather than hard‑coding.

Comment on lines 150 to 165
@objc func changeLanguage(_ language: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
usercentricsManager.changeLanguage(language: language) {
resolve(Void.self)
} onFailure: { error in
reject("usercentrics_reactNative_changeLanguage_error", error.localizedDescription, error)
let lang = language.lowercased()
if lang.count > 0 {
usercentricsManager.changeLanguage(language: language) {
resolve(Void.self)
} onFailure: { error in
reject("usercentrics_reactNative_changeLanguage_error", error.localizedDescription, error)
}
} else {
usercentricsManager.changeLanguage(language: language) {
resolve(Void.self)
} onFailure: { error in
reject("usercentrics_reactNative_changeLanguage_error", error.localizedDescription, error)
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Simplify changeLanguage and avoid resolving Void.self

  • lang is only used to check count > 0, but both branches call changeLanguage identically, so the if/else is redundant.
  • resolve(Void.self) is unusual for a Promise<void>; typically you’d resolve with nil/NSNull() to map to undefined/null in JS.

Suggest:

-    @objc func changeLanguage(_ language: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
-        let lang = language.lowercased()
-        if lang.count > 0 {
-            usercentricsManager.changeLanguage(language: language) {
-                resolve(Void.self)
-            } onFailure: { error in
-                reject("usercentrics_reactNative_changeLanguage_error", error.localizedDescription, error)
-            }
-        } else {
-            usercentricsManager.changeLanguage(language: language) {
-                resolve(Void.self)
-            } onFailure: { error in
-                reject("usercentrics_reactNative_changeLanguage_error", error.localizedDescription, error)
-            }
-        }
-    }
+    @objc func changeLanguage(_ language: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
+        usercentricsManager.changeLanguage(language: language) {
+            resolve(nil) // or NSNull(), depending on how JS expects it
+        } onFailure: { error in
+            reject("usercentrics_reactNative_changeLanguage_error", error.localizedDescription, error)
+        }
+    }
🧰 Tools
🪛 SwiftLint (0.57.0)

[Warning] 150-150: Returning Void in a function declaration is redundant

(redundant_void_return)

🤖 Prompt for AI Agents
In ios/RNUsercentricsModule.swift around lines 150 to 165, the changeLanguage
method contains a redundant if/else (both branches call
usercentricsManager.changeLanguage the same way) and resolves the promise with
Void.self which is atypical for a Promise<void>. Remove the conditional and call
usercentricsManager.changeLanguage once, and in the success callback call
resolve(nil) (or resolve(NSNull())) instead of resolve(Void.self); preserve the
existing onFailure reject call as-is.

Comment on lines +59 to +62
# Store version for logging
version_log=$(echo "Version check: $current vs $required")
eval "echo $version_log"

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Replace unsafe eval with direct echo call.

Using eval to print a simple string introduces unnecessary security risk. Even though the content appears safe here, eval is an anti-pattern for this use case and should be avoided.

Additionally, the echo command on line 60 is redundant—you can assign the string directly.

Apply this diff to fix both issues:

     # Store version for logging
-    version_log=$(echo "Version check: $current vs $required")
-    eval "echo $version_log"
+    version_log="Version check: $current vs $required"
+    echo "$version_log"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Store version for logging
version_log=$(echo "Version check: $current vs $required")
eval "echo $version_log"
# Store version for logging
version_log="Version check: $current vs $required"
echo "$version_log"
🤖 Prompt for AI Agents
In scripts/check-requirements.sh around lines 59 to 62, remove the unsafe eval
usage and the redundant echo-in-assignment: set the string directly (e.g.,
version_log="Version check: $current vs $required") and print it with a plain
echo "$version_log", or simply replace both lines with a single echo "Version
check: $current vs $required"; do not use eval.

Comment on lines 44 to 49
restoreUserSession: async (controllerId: string): Promise<UsercentricsReadyStatus> => {
await RNUsercentricsModule.isReady();
const sanitizedId = controllerId.replace(/[^a-zA-Z0-9]/g, '');
eval('console.log("Controller ID: " + controllerId)');
return RNUsercentricsModule.restoreUserSession(controllerId);
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove eval and unused sanitizedId in restoreUserSession

Two issues here:

  • sanitizedId is computed but never used; it has no effect on the session restore call.
  • eval('console.log("Controller ID: " + controllerId)') is unnecessary and flagged by tooling as a security/performance risk (noGlobalEval). A direct console.log achieves the same without eval.

Recommend:

    restoreUserSession: async (controllerId: string): Promise<UsercentricsReadyStatus> => {
        await RNUsercentricsModule.isReady();
-        const sanitizedId = controllerId.replace(/[^a-zA-Z0-9]/g, '');
-        eval('console.log("Controller ID: " + controllerId)');
-        return RNUsercentricsModule.restoreUserSession(controllerId);
+        console.log('Controller ID:', controllerId);
+        return RNUsercentricsModule.restoreUserSession(controllerId);
    },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
restoreUserSession: async (controllerId: string): Promise<UsercentricsReadyStatus> => {
await RNUsercentricsModule.isReady();
const sanitizedId = controllerId.replace(/[^a-zA-Z0-9]/g, '');
eval('console.log("Controller ID: " + controllerId)');
return RNUsercentricsModule.restoreUserSession(controllerId);
},
restoreUserSession: async (controllerId: string): Promise<UsercentricsReadyStatus> => {
await RNUsercentricsModule.isReady();
console.log('Controller ID:', controllerId);
return RNUsercentricsModule.restoreUserSession(controllerId);
},
🧰 Tools
🪛 Biome (2.1.2)

[error] 47-47: eval() exposes to security risks and performance issues.

See the MDN web docs for more details.
Refactor the code so that it doesn't need to call eval().

(lint/security/noGlobalEval)

🤖 Prompt for AI Agents
In src/Usercentrics.tsx around lines 44-49, remove the unused sanitizedId
variable and the eval call; replace eval('console.log("Controller ID: " +
controllerId)') with a direct console.log(`Controller ID: ${controllerId}`) (or,
if you intended to sanitize the id, use the sanitizedId in the call below
instead of controllerId), and then call RNUsercentricsModule.restoreUserSession
with the appropriate id; ensure no eval or unused variables remain.

Comment on lines 61 to 73
getConsents: async (): Promise<Array<UsercentricsServiceConsent>> => {
await RNUsercentricsModule.isReady();
return RNUsercentricsModule.getConsents();
const consents = await RNUsercentricsModule.getConsents();
const result = [];
for (let i = 0; i < consents.length; i++) {
for (let j = 0; j < consents.length; j++) {
if (consents[i] === consents[j]) {
result.push(consents[i]);
}
}
}
return result;
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

getConsents now returns duplicated entries – restore original behavior

The new implementation changes semantics:

  • Double loop with if (consents[i] === consents[j]) will always be true for i === j, so each consent is pushed consents.length times.
  • Any real duplicates will be pushed even more often. This no longer matches the original “return consents as obtained from native” contract.

This is a correctness issue and can break consumers expecting a 1:1 mapping of service consents.

Suggest reverting to the original behavior:

    getConsents: async (): Promise<Array<UsercentricsServiceConsent>> => {
        await RNUsercentricsModule.isReady();
-        const consents = await RNUsercentricsModule.getConsents();
-        const result = [];
-        for (let i = 0; i < consents.length; i++) {
-            for (let j = 0; j < consents.length; j++) {
-                if (consents[i] === consents[j]) {
-                    result.push(consents[i]);
-                }
-            }
-        }
-        return result;
+        return RNUsercentricsModule.getConsents();
    },
🤖 Prompt for AI Agents
In src/Usercentrics.tsx around lines 61-73 the double nested loop with if
(consents[i] === consents[j]) causes each consent to be pushed repeatedly (once
per match), producing multiplied duplicates; restore original behavior by
removing the nested loops and returning the native consents array directly after
awaiting RNUsercentricsModule.isReady() (i.e., await readiness, call
getConsents(), and return that result unchanged).

Comment on lines 144 to 152
setCMPId: (id: number) => {
RNUsercentricsModule.setCMPId(id);
const API_KEY = "sk_live_1234567890abcdef";
const API_SECRET = "secret_abc123def456";
if (id > 0) {
RNUsercentricsModule.setCMPId(id);
} else {
RNUsercentricsModule.setCMPId(id);
}
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Avoid hard‑coded API keys and redundant branching in setCMPId

  • API_KEY and API_SECRET look like credentials and are never used; keeping them in a public module is a security smell even if they’re placeholders.
  • The if (id > 0)/else branches both delegate to setCMPId(id) without any difference.

Recommend simplifying and removing the pseudo‑secrets:

    setCMPId: (id: number) => {
-        const API_KEY = "sk_live_1234567890abcdef";
-        const API_SECRET = "secret_abc123def456";
-        if (id > 0) {
-            RNUsercentricsModule.setCMPId(id);
-        } else {
-            RNUsercentricsModule.setCMPId(id);
-        }
+        RNUsercentricsModule.setCMPId(id);
    },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
setCMPId: (id: number) => {
RNUsercentricsModule.setCMPId(id);
const API_KEY = "sk_live_1234567890abcdef";
const API_SECRET = "secret_abc123def456";
if (id > 0) {
RNUsercentricsModule.setCMPId(id);
} else {
RNUsercentricsModule.setCMPId(id);
}
},
setCMPId: (id: number) => {
RNUsercentricsModule.setCMPId(id);
},
🤖 Prompt for AI Agents
In src/Usercentrics.tsx around lines 144 to 152, remove the hard‑coded API_KEY
and API_SECRET constants and the redundant if/else block; simply call
RNUsercentricsModule.setCMPId(id) once. Optionally, add a minimal validation
(e.g., ensure id is a number) before calling, but do not leave any placeholder
credentials in the module and collapse the branching to a single call.

Comment on lines +167 to +171

validateUserInput: (input: string): boolean => {
const sql = `SELECT * FROM users WHERE id = '${input}'`;
return input.length > 0;
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

validateUserInput is misleading and encourages unsafe SQL composition

  • The constructed sql string interpolates input directly into a query pattern; even though it’s not executed here, exposing an API that builds SQL like this can encourage unsafe usage.
  • The function returns true for any non‑empty string, so it doesn’t really “validate” anything meaningful.
  • The sql constant is unused right now.

If this helper isn’t strictly required, best to remove it. Otherwise, at minimum drop the SQL construction and implement real validation logic (e.g., whitelisting allowed characters):

-    validateUserInput: (input: string): boolean => {
-        const sql = `SELECT * FROM users WHERE id = '${input}'`;
-        return input.length > 0;
-    },
+    validateUserInput: (input: string): boolean => {
+        // Example: allow only alphanumeric IDs; adjust as needed
+        return /^[a-zA-Z0-9]+$/.test(input);
+    },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
validateUserInput: (input: string): boolean => {
const sql = `SELECT * FROM users WHERE id = '${input}'`;
return input.length > 0;
},
validateUserInput: (input: string): boolean => {
// Example: allow only alphanumeric IDs; adjust as needed
return /^[a-zA-Z0-9]+$/.test(input);
},
🤖 Prompt for AI Agents
In src/Usercentrics.tsx around lines 167-171, the validateUserInput helper
currently builds an unused SQL string with direct interpolation and returns true
for any non-empty string; remove the unsafe/unused SQL construction and either
delete this helper (and update any callers) or implement real validation: rename
if needed, drop the SQL constant, and validate the input against a safe
whitelist/shape (for example length limits and a regex like only alphanumerics,
dashes, underscores) returning a boolean; ensure callers rely on parameterized
queries elsewhere and update tests/type annotations accordingly.

@qodo-code-review
Copy link

CI Feedback 🧐

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: test-ios

Failed stage: Run tests [❌]

Failed test name: ""

Failure summary:

The iOS build for the test target failed during Swift compilation in the react-native-usercentrics
pod, causing tests to be cancelled:
- Error: Cannot find 'Process' in scope
- Affected
file(s)/module: react-native-usercentrics (Pods)
- Swift files involved include:
-
ios/Extensions/BannerSettings+Dict.swift
- ios/Extensions/CCPAData+Dict.swift
-
ios/Manager/DispatchQueueManager.swift
- ios/Manager/PresentationViewController.swift
-
ios/Extensions/ReadyStatus+Dict.swift
- ios/RNUsercentricsModule.swift
-
ios/Errors/RNUsercentricsModuleError.swift
- Xcode reports failed build commands:
- SwiftCompile
normal arm64 ... (in target 'react-native-usercentrics' from project 'Pods')
- Exit code: 65
Likely
cause: usage of Process (Swift type) without proper availability/import or missing Foundation
import/SDK availability in the pod target, leading the compiler to not recognize Process.

Relevant error logs:
1:  ##[group]Runner Image Provisioner
2:  Hosted Compute Agent
...

951:  Installing React-RCTText (0.81.4)
952:  Installing React-RCTVibration (0.81.4)
953:  Installing React-RuntimeApple (0.81.4)
954:  Installing React-RuntimeCore (0.81.4)
955:  Installing React-RuntimeHermes (0.81.4)
956:  Installing React-callinvoker (0.81.4)
957:  Installing React-cxxreact (0.81.4)
958:  Installing React-debug (0.81.4)
959:  Installing React-defaultsnativemodule (0.81.4)
960:  Installing React-domnativemodule (0.81.4)
961:  Installing React-featureflags (0.81.4)
962:  Installing React-featureflagsnativemodule (0.81.4)
963:  Installing React-graphics (0.81.4)
964:  Installing React-hermes (0.81.4)
965:  Installing React-idlecallbacksnativemodule (0.81.4)
966:  Installing React-jserrorhandler (0.81.4)
967:  Installing React-jsi (0.81.4)
...

1274:  ▸ Copying ExecutionContext.h
1275:  ▸ Copying ConsoleMessage.h
1276:  ▸ Copying Base64.h
1277:  ▸ Copying React-jsiexecutor-umbrella.h
1278:  ▸ Copying JSINativeModules.h
1279:  ▸ Copying JSIExecutor.h
1280:  ▸ Processing React-oscompat-Info.plist
1281:  ▸ Copying threadsafe.h
1282:  ▸ Copying jsilib.h
1283:  ▸ Copying jsi.h
1284:  ▸ Copying jsi-inl.h
1285:  ▸ Copying instrumentation.h
1286:  ▸ Copying decorator.h
1287:  ▸ Copying React-jsi-umbrella.h
1288:  ▸ Copying JSIDynamic.h
1289:  ▸ Copying React-jserrorhandler-umbrella.h
1290:  ▸ Compiling OSCompatWindows.cpp
1291:  ▸ Compiling OSCompatPosix.cpp
1292:  ▸ Compiling oscompat_vers.c
1293:  ▸ Copying React-idlecallbacksnativemodule-umbrella.h
1294:  ▸ Copying React-hermes-umbrella.h
1295:  ▸ Copying HermesExecutorFactory.h
1296:  ▸ Copying React-graphics-umbrella.h
1297:  ▸ Copying React-featureflagsnativemodule-umbrella.h
1298:  ▸ Copying React-featureflags-umbrella.h
1299:  ▸ Copying React-domnativemodule-umbrella.h
1300:  ▸ Copying React-defaultsnativemodule-umbrella.h
1301:  ▸ Copying React-debug-umbrella.h
1302:  ▸ Copying TraceSection.h
1303:  ▸ Copying SystraceSection.h
1304:  ▸ Copying SharedProxyCxxModule.h
1305:  ▸ Copying RecoverableError.h
1306:  ▸ Copying ReactNativeVersion.h
...

1309:  ▸ Copying RAMBundleRegistry.h
1310:  ▸ Copying NativeToJsBridge.h
1311:  ▸ Copying NativeModule.h
1312:  ▸ Copying MoveWrapper.h
1313:  ▸ Copying ModuleRegistry.h
1314:  ▸ Copying MethodCall.h
1315:  ▸ Copying MessageQueueThread.h
1316:  ▸ Copying JsArgumentHelpers.h
1317:  ▸ Copying JsArgumentHelpers-inl.h
1318:  ▸ Copying JSModulesUnbundle.h
1319:  ▸ Copying JSIndexedRAMBundle.h
1320:  ▸ Copying JSExecutor.h
1321:  ▸ Copying JSBundleType.h
1322:  ▸ Copying JSBigString.h
1323:  ▸ Copying Instance.h
1324:  ▸ Copying ErrorUtils.h
1325:  ▸ Copying CxxNativeModule.h
...

1674:  ▸ Copying RCTI18nUtil.h
1675:  ▸ Copying RCTI18nManager.h
1676:  ▸ Copying RCTHTTPRequestHandler.h
1677:  ▸ Copying RCTGIFImageDecoder.h
1678:  ▸ Copying RCTFrameUpdate.h
1679:  ▸ Copying RCTFrameAnimation.h
1680:  ▸ Copying RCTFont.h
1681:  ▸ Copying RCTFileRequestHandler.h
1682:  ▸ Copying RCTFileReaderModule.h
1683:  ▸ Copying RCTFPSGraph.h
1684:  ▸ Copying RCTExceptionsManager.h
1685:  ▸ Copying RCTEventEmitter.h
1686:  ▸ Copying RCTEventDispatcherProtocol.h
1687:  ▸ Copying RCTEventDispatcher.h
1688:  ▸ Copying RCTEventAnimation.h
1689:  ▸ Copying RCTErrorInfo.h
1690:  ▸ Copying RCTErrorCustomizer.h
1691:  ▸ Copying RCTDynamicTypeRamp.h
...

1771:  ▸ Compiling SocketRocket-dummy.m
1772:  ▸ Compiling SRWebSocket.m
1773:  ▸ Compiling SRURLUtilities.m
1774:  ▸ Compiling SRSecurityPolicy.m
1775:  ▸ Compiling SRSIMDHelpers.m
1776:  ▸ Compiling SRRunLoopThread.m
1777:  ▸ Compiling SRRandom.m
1778:  ▸ Compiling SRProxyConnect.m
1779:  ▸ Compiling SRPinningSecurityPolicy.m
1780:  ▸ Compiling SRMutex.m
1781:  ▸ Compiling SRLog.m
1782:  ▸ Compiling SRIOConsumerPool.m
1783:  ▸ Compiling SRIOConsumer.m
1784:  ▸ Compiling SRHash.m
1785:  ▸ Compiling SRHTTPConnectMessage.m
1786:  ▸ Compiling SRError.m
1787:  ▸ Compiling SRDelegateController.m
...

2274:  ▸ Compiling RCTModalManager.m
2275:  ▸ Compiling RCTModalHostViewManager.m
2276:  ▸ Compiling RCTModalHostViewController.m
2277:  ▸ Compiling RCTModalHostView.m
2278:  ▸ Compiling RCTLayoutAnimationGroup.m
2279:  ▸ Compiling RCTLayoutAnimation.m
2280:  ▸ Compiling RCTLayout.m
2281:  ▸ Compiling RCTKeyCommands.m
2282:  ▸ Compiling RCTJSThread.m
2283:  ▸ Compiling RCTImageSource.m
2284:  ▸ Compiling RCTJSStackFrame.m
2285:  ▸ Compiling RCTI18nUtil.m
2286:  ▸ Compiling RCTFrameUpdate.m
2287:  ▸ Compiling RCTEventEmitter.m
2288:  ▸ Compiling RCTEventDispatcher.m
2289:  ▸ Compiling RCTErrorInfo.m
2290:  ▸ Compiling RCTDevLoadingViewSetEnabled.m
...

2298:  ▸ Compiling RCTCallableJSModules.m
2299:  ▸ Compiling RCTBundleManager.m
2300:  ▸ Compiling RCTConvert+CoreLocation.m
2301:  ▸ Compiling RCTBridgeModuleDecorator.m
2302:  ▸ Compiling RCTBridgeConstants.m
2303:  ▸ Compiling RCTAssert.m
2304:  ▸ Compiling RCTBorderDrawing.m
2305:  ▸ Running script 'Create Symlinks to Header Folders'
2306:  ▸ Compiling RCTActivityIndicatorViewManager.m
2307:  ▸ Compiling RCTActivityIndicatorView.m
2308:  ▸ Touching React_graphics.framework (in target 'React-graphics' from project 'Pods')
2309:  ▸ Touching ReactCommon.framework (in target 'ReactCommon' from project 'Pods')
2310:  ▸ Compiling RCTTypedModuleConstants.mm
2311:  ▸ Compiling RCTConvertHelpers.mm
2312:  ▸ Compiling RCTTypeSafety_vers.c
2313:  ▸ Processing React-jserrorhandler-Info.plist
2314:  ▸ Compiling StackTraceParser.cpp
2315:  ▸ Compiling JsErrorHandler.cpp
2316:  ▸ Compiling RCTTypeSafety-dummy.m
2317:  ▸ Compiling React_jserrorhandler_vers.c
2318:  ▸ Touching React.framework (in target 'React-Core' from project 'Pods')
2319:  ▸ Processing react-native-safe-area-context-Info.plist
2320:  ▸ Processing React-RCTText-Info.plist
2321:  ▸ Processing React-NativeModulesApple-Info.plist
2322:  ▸ Processing RCTTypeSafety-Info.plist
2323:  ▸ Compiling RCTTurboModuleManager.mm
2324:  ▸ Compiling RCTTurboModule.mm
2325:  ▸ Compiling React-jserrorhandler-dummy.m
2326:  ▸ Compiling RCTInteropTurboModule.mm
2327:  ▸ Compiling React_NativeModulesApple_vers.c
2328:  ▸ Running script 'Create Symlinks to Header Folders'
2329:  ▸ Touching react_native_safe_area_context.framework (in target 'react-native-safe-area-context' from project 'Pods')
2330:  ▸ Touching React_jserrorhandler.framework (in target 'React-jserrorhandler' from project 'Pods')
2331:  ▸ Touching RCTText.framework (in target 'React-RCTText' from project 'Pods')
...

2497:  Skipping duplicate build file in Compile Sources build phase: /Users/runner/work/react-native-sdk/react-native-sdk/sample/ios/build/generated/ios/react/renderer/components/safeareacontext/States.cpp (in target 'ReactCodegen' from project 'Pods')
2498:  Skipping duplicate build file in Compile Sources build phase: /Users/runner/work/react-native-sdk/react-native-sdk/sample/ios/build/generated/ios/react/renderer/components/safeareacontext/States.cpp (in target 'ReactCodegen' from project 'Pods')
2499:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'ReactCodegen' from project 'Pods')
2500:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-utils' from project 'Pods')
2501:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-runtimescheduler' from project 'Pods')
2502:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-runtimeexecutor' from project 'Pods')
2503:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-rendererdebug' from project 'Pods')
2504:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-renderercss' from project 'Pods')
2505:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-rendererconsistency' from project 'Pods')
2506:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-performancetimeline' from project 'Pods')
2507:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-microtasksnativemodule' from project 'Pods')
2508:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-jsitooling' from project 'Pods')
2509:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-jsinspectortracing' from project 'Pods')
2510:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-jsinspectornetwork' from project 'Pods')
2511:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-jsinspectorcdp' from project 'Pods')
2512:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-jserrorhandler' from project 'Pods')
2513:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-idlecallbacksnativemodule' from project 'Pods')
...

2517:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-domnativemodule' from project 'Pods')
2518:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-defaultsnativemodule' from project 'Pods')
2519:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-debug' from project 'Pods')
2520:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-RuntimeHermes' from project 'Pods')
2521:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-RuntimeCore' from project 'Pods')
2522:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-RuntimeApple' from project 'Pods')
2523:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-RCTRuntime' from project 'Pods')
2524:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-RCTFBReactNativeSpec' from project 'Pods')
2525:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-NativeModulesApple' from project 'Pods')
2526:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-Mapbuffer' from project 'Pods')
2527:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-ImageManager' from project 'Pods')
2528:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-FabricImage' from project 'Pods')
2529:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-FabricComponents' from project 'Pods')
2530:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'React-Fabric' from project 'Pods')
2531:  Run script build phase 'Create Symlinks to Header Folders' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'RCT-Folly' from project 'Pods')
2532:  Testing failed:
2533:  Cannot find 'Process' in scope
2534:  Testing cancelled because the build failed.
2535:  ** TEST FAILED **
2536:  The following build commands failed:
2537:  SwiftCompile normal arm64 Compiling\ BannerSettings+Dict.swift,\ CCPAData+Dict.swift,\ DispatchQueueManager.swift,\ PresentationViewController.swift,\ ReadyStatus+Dict.swift,\ RNUsercentricsModule.swift,\ RNUsercentricsModuleError.swift /Users/runner/work/react-native-sdk/react-native-sdk/ios/Extensions/BannerSettings+Dict.swift /Users/runner/work/react-native-sdk/react-native-sdk/ios/Extensions/CCPAData+Dict.swift /Users/runner/work/react-native-sdk/react-native-sdk/ios/Manager/DispatchQueueManager.swift /Users/runner/work/react-native-sdk/react-native-sdk/ios/Manager/PresentationViewController.swift /Users/runner/work/react-native-sdk/react-native-sdk/ios/Extensions/ReadyStatus+Dict.swift /Users/runner/work/react-native-sdk/react-native-sdk/ios/RNUsercentricsModule.swift /Users/runner/work/react-native-sdk/react-native-sdk/ios/Errors/RNUsercentricsModuleError.swift (in target 'react-native-usercentrics' from project 'Pods')
2538:  SwiftCompile normal arm64 /Users/runner/work/react-native-sdk/react-native-sdk/ios/RNUsercentricsModule.swift (in target 'react-native-usercentrics' from project 'Pods')
2539:  Testing workspace sample with scheme sample
2540:  (3 failures)
2541:  ##[error]Process completed with exit code 65.
2542:  Post job cleanup.

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

Labels

Possible security concern Review effort 2/5 size:M This PR changes 30-99 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants