Skip to content

Conversation

@akhil-testsigma
Copy link
Contributor

@akhil-testsigma akhil-testsigma commented Sep 23, 2025

Addon Name: Dynamic gRPC Client
Jarvis Link: https://jarvis.testsigma.com/ui/tenants/3072/addons
Jira : https://testsigma.atlassian.net/browse/TE-29598
Added Dynamic gRPC Client Action with JSON Request Support

Summary by CodeRabbit

  • New Features

    • Added dynamic gRPC client actions across Web, Android, iOS, and REST API. Users can provide a proto, service/method, endpoint, and JSON request to perform unary calls at runtime and capture the JSON response in a runtime variable. Includes validation, error handling, and automatic cleanup.
  • Chores

    • Introduced a new build module with dependencies and plugins to produce a packaged artifact and attached sources.
    • Added configuration properties to support SDK integration.

@coderabbitai
Copy link

coderabbitai bot commented Sep 23, 2025

Walkthrough

Adds a new Maven module dynamic_grpc_client with dependencies/plugins and resource configuration. Introduces platform-specific GrpcClientAction classes (android, ios, web, restapi) that compile .proto files at runtime, build dynamic requests from JSON, perform unary gRPC calls, and store JSON responses into runtime variables, with cleanup and error handling.

Changes

Cohort / File(s) Summary
Module setup and config
dynamic_grpc_client/pom.xml, dynamic_grpc_client/src/main/resources/testsigma-sdk.properties
New Maven module with Java 11, dependencies (gRPC, protobuf, Selenium, Appium, SDK, etc.), shade/source plugins, and SDK API key property.
Dynamic gRPC client actions
dynamic_grpc_client/src/main/java/com/testsigma/addons/.../GrpcClientAction.java (android/GrpcClientAction.java, ios/GrpcClientAction.java, restapi/GrpcClientAction.java, web/GrpcClientAction.java)
New GrpcClientAction implementations that generate descriptor sets via protoc, load service/method descriptors, build DynamicMessage from JSON, perform unary gRPC calls over plaintext, serialize response to JSON, assign to runtime variable, and handle cleanup/errors.

Sequence Diagram(s)

sequenceDiagram
  actor T as Test Runner
  participant A as GrpcClientAction
  participant P as Embedded protoc
  participant D as Descriptor Loader
  participant S as gRPC Server

  T->>A: execute(protoPath, url, service, method, json, variable)
  A->>P: generateDescriptor(protoDir, protoFile)
  P-->>A: descriptorSetFile
  A->>D: load(service, method) from descriptor
  D-->>A: MethodDescriptor
  A->>A: sanitize/parse JSON -> DynamicMessage
  A->>S: unary call(MethodDescriptor, request)
  S-->>A: DynamicMessage response
  A->>A: to JSON
  A-->>T: set runtime variable, Result.SUCCESS
  A->>A: shutdown channel, delete temp descriptor
  note over A: On error: set message, Result.FAILED
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • Ganesh-Testsigma
  • vigneshtestsigma

Poem

I hopped through fields of proto light,
Compiling dreams in bytes so bright.
A JSON nibble, a channel spun—
Unary whispers, request is done.
Response like clover, sweet and clear,
I stash it safe, a souvenir.
Thump-thump—new trails of gRPC cheer! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly states the primary change—adding a Dynamic gRPC Client Action with JSON request support—which aligns with the diff that introduces multiple GrpcClientAction implementations and a new module POM; it is specific and not vague. It communicates the main intent to reviewers and links to the related work by including the issue key. The 'feat/' prefix and issue ID are slightly noisy but do not make the title misleading.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/TE-29598-Added-Dynamic-gRPC-Client-Action-with-JSON-Request-Support

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


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.

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: 9

🧹 Nitpick comments (19)
dynamic_grpc_client/src/main/java/com/testsigma/addons/ios/GrpcClientAction.java (4)

68-73: Validate proto file and handle null parent path.

Avoid NPE and clearer error if the file is missing.

Apply:

-            File protoFile = new File(protoFilePath.getValue().toString());
-            String protoDir = protoFile.getParent();
+            File protoFile = new File(protoFilePath.getValue().toString());
+            if (!protoFile.exists()) {
+                throw new java.io.FileNotFoundException("Proto file not found: " + protoFile.getPath());
+            }
+            String protoDir = protoFile.getParent();
+            if (protoDir == null) {
+                protoDir = protoFile.getAbsoluteFile().getParent();
+            }
             String protoFileName = protoFile.getName();

162-172: Guard against non-unary RPCs.

Fail early if the target method is streaming; the action constructs a UNARY descriptor.

-    private MethodDescriptor<DynamicMessage, DynamicMessage> buildGrpcMethod(Descriptors.MethodDescriptor methodDescriptor) {
+    private MethodDescriptor<DynamicMessage, DynamicMessage> buildGrpcMethod(Descriptors.MethodDescriptor methodDescriptor) {
+        if (methodDescriptor.isClientStreaming() || methodDescriptor.isServerStreaming()) {
+            throw new UnsupportedOperationException("Only unary gRPC methods are supported by this action.");
+        }
         return MethodDescriptor.<DynamicMessage, DynamicMessage>newBuilder()

174-178: Add per-call deadline to avoid indefinite blocking.

Prevent hangs when the server is slow/unreachable.

-        return ClientCalls.blockingUnaryCall(channel, method, CallOptions.DEFAULT, request);
+        return ClientCalls.blockingUnaryCall(
+                channel, method, CallOptions.DEFAULT.withDeadlineAfter(30, TimeUnit.SECONDS), request);

83-86: Consider TLS configurability (avoid unconditional plaintext).

Expose a flag or detect scheme to use TLS (useTransportSecurity) when needed; plaintext-only limits secure endpoints.

dynamic_grpc_client/src/main/java/com/testsigma/addons/web/GrpcClientAction.java (3)

162-172: Fail if method is streaming.

Unary-only action; add a guard.

-    private MethodDescriptor<DynamicMessage, DynamicMessage> buildGrpcMethod(Descriptors.MethodDescriptor methodDescriptor) {
+    private MethodDescriptor<DynamicMessage, DynamicMessage> buildGrpcMethod(Descriptors.MethodDescriptor methodDescriptor) {
+        if (methodDescriptor.isClientStreaming() || methodDescriptor.isServerStreaming()) {
+            throw new UnsupportedOperationException("Only unary gRPC methods are supported by this action.");
+        }
         return MethodDescriptor.<DynamicMessage, DynamicMessage>newBuilder()

174-178: Add deadline to the RPC call.

Avoid indefinite blocking.

-        return ClientCalls.blockingUnaryCall(channel, method, CallOptions.DEFAULT, request);
+        return ClientCalls.blockingUnaryCall(
+                channel, method, CallOptions.DEFAULT.withDeadlineAfter(30, TimeUnit.SECONDS), request);

83-86: Make TLS optional/configurable.

Don’t hardcode plaintext; allow secure endpoints.

dynamic_grpc_client/src/main/java/com/testsigma/addons/android/GrpcClientAction.java (3)

162-172: Guard against streaming RPCs.

Support unary only.

-    private MethodDescriptor<DynamicMessage, DynamicMessage> buildGrpcMethod(Descriptors.MethodDescriptor methodDescriptor) {
+    private MethodDescriptor<DynamicMessage, DynamicMessage> buildGrpcMethod(Descriptors.MethodDescriptor methodDescriptor) {
+        if (methodDescriptor.isClientStreaming() || methodDescriptor.isServerStreaming()) {
+            throw new UnsupportedOperationException("Only unary gRPC methods are supported by this action.");
+        }
         return MethodDescriptor.<DynamicMessage, DynamicMessage>newBuilder()

174-178: Add RPC deadline.

Avoid hangs on network issues.

-        return ClientCalls.blockingUnaryCall(channel, method, CallOptions.DEFAULT, request);
+        return ClientCalls.blockingUnaryCall(
+                channel, method, CallOptions.DEFAULT.withDeadlineAfter(30, TimeUnit.SECONDS), request);

83-86: TLS support.

Expose a toggle to use TLS instead of plaintext.

dynamic_grpc_client/pom.xml (5)

37-41: Use a stable JUnit 5 release (avoid milestone).

Upgrade from 5.8.0-M1 to a stable version.

-            <version>${junit.jupiter.version}</version>
+            <version>5.11.3</version>

43-46: Bump TestNG to address known vulnerability.

6.14.3 has a path traversal issue (GHSA-rc2q-x9mf-w3vf). Upgrade to 7.x.

-            <version>6.14.3</version>
+            <version>7.10.2</version>

60-63: Update Jackson Annotations.

2.13.0 is outdated; align to current stable to pick up fixes.

-            <version>2.13.0</version>
+            <version>2.17.2</version>

99-111: Shade relocation to avoid dependency conflicts.

Relocating common packages reduces classpath clashes when embedding.

Example configuration (adjust as needed):

             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-shade-plugin</artifactId>
                 <version>3.2.4</version>
                 <executions>
                     <execution>
                         <phase>package</phase>
                         <goals>
                             <goal>shade</goal>
                         </goals>
+                        <configuration>
+                            <relocations>
+                                <relocation>
+                                    <pattern>io.grpc</pattern>
+                                    <shadedPattern>com.testsigma.shadow.io.grpc</shadedPattern>
+                                </relocation>
+                                <relocation>
+                                    <pattern>com.google.protobuf</pattern>
+                                    <shadedPattern>com.testsigma.shadow.com.google.protobuf</shadedPattern>
+                                </relocation>
+                            </relocations>
+                        </configuration>
                     </execution>
                 </executions>
             </plugin>

24-95: Consider resource filtering for secrets.

If a properties file is required, inject secrets via env/CI and Maven filtering, not in VCS.

Add:

<build>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
      <filtering>true</filtering>
    </resource>
  </resources>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-resources-plugin</artifactId>
      <version>3.3.1</version>
    </plugin>
  </plugins>
</build>

Then reference ${TESTSIGMA_SDK_API_KEY} in a non-committed template if needed.

dynamic_grpc_client/src/main/java/com/testsigma/addons/restapi/GrpcClientAction.java (4)

68-73: Handle null proto parent directory.

When the proto path has no parent, protoDir is null and protoc args break.

-            String protoDir = protoFile.getParent();
+            String protoDir = protoFile.getParent();
+            if (protoDir == null) {
+                protoDir = ".";
+            }

83-86: Support TLS optionally; avoid hard-coded plaintext.

Allow grpcs by toggling transport security based on the target string.

-            channel = ManagedChannelBuilder.forTarget(grpcUrl.getValue().toString())
-                    .usePlaintext()
-                    .build();
+            String target = grpcUrl.getValue().toString();
+            ManagedChannelBuilder<?> channelBuilder = ManagedChannelBuilder.forTarget(target);
+            if (target.startsWith("grpcs://")) {
+                channelBuilder = channelBuilder.useTransportSecurity();
+            } else {
+                channelBuilder = channelBuilder.usePlaintext();
+            }
+            channel = channelBuilder.build();

Also clarify in the action docs that:

  • Use grpcs://host:port to enable TLS, grpc:// or host:port for plaintext.
  • If auth is needed, consider adding optional headers/metadata in a follow-up.

74-76: Guard against streaming methods (currently only UNARY is supported).

Fail fast with a clear message if the method is client/server streaming.

             Descriptors.MethodDescriptor methodDescriptor =
                     loadServiceDescriptors(tempDescriptorFile, serviceName.getValue().toString(), methodName.getValue().toString());
 
+            if (methodDescriptor.isClientStreaming() || methodDescriptor.isServerStreaming()) {
+                throw new UnsupportedOperationException("Only UNARY gRPC methods are supported by this action.");
+            }

97-101: Reduce stack traces at warn level.

Consider logging e.getMessage() at warn and full stack at debug to keep logs cleaner.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 192562b and d991024.

📒 Files selected for processing (6)
  • dynamic_grpc_client/pom.xml (1 hunks)
  • dynamic_grpc_client/src/main/java/com/testsigma/addons/android/GrpcClientAction.java (1 hunks)
  • dynamic_grpc_client/src/main/java/com/testsigma/addons/ios/GrpcClientAction.java (1 hunks)
  • dynamic_grpc_client/src/main/java/com/testsigma/addons/restapi/GrpcClientAction.java (1 hunks)
  • dynamic_grpc_client/src/main/java/com/testsigma/addons/web/GrpcClientAction.java (1 hunks)
  • dynamic_grpc_client/src/main/resources/testsigma-sdk.properties (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
dynamic_grpc_client/src/main/java/com/testsigma/addons/ios/GrpcClientAction.java (3)
dynamic_grpc_client/src/main/java/com/testsigma/addons/android/GrpcClientAction.java (1)
  • Data (33-194)
dynamic_grpc_client/src/main/java/com/testsigma/addons/restapi/GrpcClientAction.java (1)
  • Data (33-194)
dynamic_grpc_client/src/main/java/com/testsigma/addons/web/GrpcClientAction.java (1)
  • Data (33-194)
dynamic_grpc_client/src/main/java/com/testsigma/addons/web/GrpcClientAction.java (3)
dynamic_grpc_client/src/main/java/com/testsigma/addons/android/GrpcClientAction.java (1)
  • Data (33-194)
dynamic_grpc_client/src/main/java/com/testsigma/addons/ios/GrpcClientAction.java (1)
  • Data (33-194)
dynamic_grpc_client/src/main/java/com/testsigma/addons/restapi/GrpcClientAction.java (1)
  • Data (33-194)
dynamic_grpc_client/src/main/java/com/testsigma/addons/android/GrpcClientAction.java (3)
dynamic_grpc_client/src/main/java/com/testsigma/addons/ios/GrpcClientAction.java (1)
  • Data (33-194)
dynamic_grpc_client/src/main/java/com/testsigma/addons/restapi/GrpcClientAction.java (1)
  • Data (33-194)
dynamic_grpc_client/src/main/java/com/testsigma/addons/web/GrpcClientAction.java (1)
  • Data (33-194)
dynamic_grpc_client/src/main/java/com/testsigma/addons/restapi/GrpcClientAction.java (2)
dynamic_grpc_client/src/main/java/com/testsigma/addons/android/GrpcClientAction.java (1)
  • Data (33-194)
dynamic_grpc_client/src/main/java/com/testsigma/addons/ios/GrpcClientAction.java (1)
  • Data (33-194)
🪛 Gitleaks (8.28.0)
dynamic_grpc_client/src/main/resources/testsigma-sdk.properties

[high] 1-1: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)

🪛 OSV Scanner (2.2.2)
dynamic_grpc_client/pom.xml

[HIGH] 1-1: com.google.protobuf:protobuf-java 3.22.3: protobuf-java has potential Denial of Service issue

(GHSA-735f-pc8j-v9w8)


[HIGH] 1-1: org.json:json 20160810: json stack overflow vulnerability

(GHSA-3vqj-43w4-2q58)


[HIGH] 1-1: org.json:json 20160810: Java: DoS Vulnerability in JSON-JAVA

(GHSA-4jq9-2xhw-jpx7)


[HIGH] 1-1: org.testng:testng 6.14.3: TestNG is vulnerable to Path Traversal

(GHSA-rc2q-x9mf-w3vf)

🔇 Additional comments (1)
dynamic_grpc_client/src/main/java/com/testsigma/addons/restapi/GrpcClientAction.java (1)

39-40: Verify base class vs ApplicationType.

Class extends WebAction while applicationType is REST_API. Confirm this pairing is intended in the SDK (or switch to a REST_API-specific base, if available).

Would you like me to propagate any necessary base-class adjustments across android/ios/web variants for consistency?

Comment on lines +65 to +78
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.57.2</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.57.2</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.57.2</version>
</dependency>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Verify gRPC versions and update if needed.

1.57.2 is behind; consider upgrading all gRPC artifacts in lockstep.


🌐 Web query:

What is the latest stable version of gRPC Java (grpc-netty-shaded, grpc-protobuf, grpc-stub), and are there known CVEs affecting 1.57.2?

💡 Result:

Short answer

  • Latest stable grpc-java release series is newer than 1.57.2 (see project releases); current releases in 2025 are in the 1.7x line (examples: v1.72.0). [1][2]
  • Known security issues relevant to 1.57.2:
    • CVE-2024-7246 (HPACK table poisoning / header-index corruption) was fixed in gRPC Java starting at 1.58.3 (and later stable fixes). 1.57.2 is vulnerable. [3]
    • CVE-2023-33953 (HPACK/DoS) is a gRPC issue that affected several implementations; the published advisories indicate the C/C++/other implementations were affected and fixes were released in the 1.56/1.57 timeframe — Java was not listed as affected in the grpc-java release notes (treat as not applicable to grpc-java 1.57.2 unless your dependency scanner flags it). [4]
    • CVE-2024-7254 is a Google Protobuf parsing (stack overflow/DoS) issue in protobuf libraries; because grpc-protobuf/grpc-protobuf-lite depend on protobuf, services using untrusted protobuf input with older protobuf versions may be vulnerable — ensure protobuf is upgraded to the fixed protobuf versions. 1.57.2 pulls older protobuf transitive versions that may require an explicit protobuf upgrade. [5]

Recommendations

  • Upgrade grpc-java artifacts (grpc-netty-shaded, grpc-protobuf, grpc-stub) to a currently supported 1.7x+ release that includes the fixes (e.g., 1.58.3+ for the HPACK fix and the latest grpc-java release for protobuf dependency updates). Verify the exact newest Maven/GitHub release before upgrading. [1][2][3][5]

Sources

  • grpc-java releases (GitHub). [1]
  • Maven Central repository listing for grpc-netty-shaded (example release metadata). [2]
  • NVD / gRPC advisory for HPACK/HPACK-table issue (CVE-2024-7246). [3]
  • Published advisory / CVE details for CVE-2023-33953. [4]
  • CVE / advisories for protobuf parsing vulnerability (CVE-2024-7254). [5]

If you want, I can: (A) look up the single newest published Maven coordinates for grpc-netty-shaded / grpc-protobuf / grpc-stub right now and give the exact version numbers to upgrade to, or (B) produce a short Gradle/Maven dependency snippet that pins safe versions. Which would you prefer?


Upgrade gRPC artifacts to a patched release (>= 1.58.3)

  • 1.57.2 is vulnerable to CVE-2024-7246 (HPACK header-index poisoning) — fixed in grpc-java 1.58.3+. Upgrade grpc-netty-shaded, grpc-protobuf, grpc-stub in lockstep (prefer the latest 1.7x stable release).
  • Explicitly bump protobuf to a patched release to address protobuf parsing CVEs (e.g., CVE-2024-7254) and re-scan transitive deps.

Location: dynamic_grpc_client/pom.xml (lines 65–78)

Comment on lines +68 to +73
File protoFile = new File(protoFilePath.getValue().toString());
String protoDir = protoFile.getParent();
String protoFileName = protoFile.getName();

tempDescriptorFile = generateDescriptor(protoDir, protoFileName);

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Check proto existence and null parent path.

Parity with web/iOS.

-            File protoFile = new File(protoFilePath.getValue().toString());
-            String protoDir = protoFile.getParent();
+            File protoFile = new File(protoFilePath.getValue().toString());
+            if (!protoFile.exists()) {
+                throw new java.io.FileNotFoundException("Proto file not found: " + protoFile.getPath());
+            }
+            String protoDir = protoFile.getParent();
+            if (protoDir == null) {
+                protoDir = protoFile.getAbsoluteFile().getParent();
+            }
             String protoFileName = protoFile.getName();
📝 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
File protoFile = new File(protoFilePath.getValue().toString());
String protoDir = protoFile.getParent();
String protoFileName = protoFile.getName();
tempDescriptorFile = generateDescriptor(protoDir, protoFileName);
File protoFile = new File(protoFilePath.getValue().toString());
if (!protoFile.exists()) {
throw new java.io.FileNotFoundException("Proto file not found: " + protoFile.getPath());
}
String protoDir = protoFile.getParent();
if (protoDir == null) {
protoDir = protoFile.getAbsoluteFile().getParent();
}
String protoFileName = protoFile.getName();
tempDescriptorFile = generateDescriptor(protoDir, protoFileName);
🤖 Prompt for AI Agents
In
dynamic_grpc_client/src/main/java/com/testsigma/addons/android/GrpcClientAction.java
around lines 68 to 73, add defensive checks before using the proto file: ensure
protoFilePath.getValue() is not null, ensure the File created from it exists and
is a file, and handle a null parent by either using the current directory (".")
or failing with a clear error; if any check fails, throw or log a descriptive
exception and abort descriptor generation rather than calling generateDescriptor
with invalid inputs.

Comment on lines +139 to +160
private Descriptors.MethodDescriptor loadServiceDescriptors(Path descriptorPath, String serviceName, String methodName) throws Exception {
try (FileInputStream descriptorStream = new FileInputStream(descriptorPath.toFile())) {
DescriptorProtos.FileDescriptorSet descriptorSet = DescriptorProtos.FileDescriptorSet.parseFrom(descriptorStream);
if (descriptorSet.getFileCount() == 0) {
throw new RuntimeException("Descriptor set is empty. Check your proto file.");
}
DescriptorProtos.FileDescriptorProto fileDescriptorProto = descriptorSet.getFile(0);

Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor.buildFrom(fileDescriptorProto, new Descriptors.FileDescriptor[]{});

Descriptors.ServiceDescriptor serviceDescriptor = fileDescriptor.findServiceByName(serviceName);
if (serviceDescriptor == null) {
throw new RuntimeException("Service not found: " + serviceName);
}

Descriptors.MethodDescriptor methodDescriptor = serviceDescriptor.findMethodByName(methodName);
if (methodDescriptor == null) {
throw new RuntimeException("Method not found: " + methodName);
}
return methodDescriptor;
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Descriptor dependency resolution needed.

Same issue as other modules.

Replace method body with the dependency-aware version and add imports:

  • java.util.Map, HashMap, Objects

Comment on lines +139 to +160
private Descriptors.MethodDescriptor loadServiceDescriptors(Path descriptorPath, String serviceName, String methodName) throws Exception {
try (FileInputStream descriptorStream = new FileInputStream(descriptorPath.toFile())) {
DescriptorProtos.FileDescriptorSet descriptorSet = DescriptorProtos.FileDescriptorSet.parseFrom(descriptorStream);
if (descriptorSet.getFileCount() == 0) {
throw new RuntimeException("Descriptor set is empty. Check your proto file.");
}
DescriptorProtos.FileDescriptorProto fileDescriptorProto = descriptorSet.getFile(0);

Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor.buildFrom(fileDescriptorProto, new Descriptors.FileDescriptor[]{});

Descriptors.ServiceDescriptor serviceDescriptor = fileDescriptor.findServiceByName(serviceName);
if (serviceDescriptor == null) {
throw new RuntimeException("Service not found: " + serviceName);
}

Descriptors.MethodDescriptor methodDescriptor = serviceDescriptor.findMethodByName(methodName);
if (methodDescriptor == null) {
throw new RuntimeException("Method not found: " + methodName);
}
return methodDescriptor;
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Load descriptors with dependencies; current logic breaks with imports.

Using only descriptorSet.getFile(0) and empty deps fails for imported types.

Apply:

-    private Descriptors.MethodDescriptor loadServiceDescriptors(Path descriptorPath, String serviceName, String methodName) throws Exception {
-        try (FileInputStream descriptorStream = new FileInputStream(descriptorPath.toFile())) {
-            DescriptorProtos.FileDescriptorSet descriptorSet = DescriptorProtos.FileDescriptorSet.parseFrom(descriptorStream);
-            if (descriptorSet.getFileCount() == 0) {
-                throw new RuntimeException("Descriptor set is empty. Check your proto file.");
-            }
-            DescriptorProtos.FileDescriptorProto fileDescriptorProto = descriptorSet.getFile(0);
-
-            Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor.buildFrom(fileDescriptorProto, new Descriptors.FileDescriptor[]{});
-
-            Descriptors.ServiceDescriptor serviceDescriptor = fileDescriptor.findServiceByName(serviceName);
-            if (serviceDescriptor == null) {
-                throw new RuntimeException("Service not found: " + serviceName);
-            }
-
-            Descriptors.MethodDescriptor methodDescriptor = serviceDescriptor.findMethodByName(methodName);
-            if (methodDescriptor == null) {
-                throw new RuntimeException("Method not found: " + methodName);
-            }
-            return methodDescriptor;
-        }
-    }
+    private Descriptors.MethodDescriptor loadServiceDescriptors(Path descriptorPath, String serviceName, String methodName) throws Exception {
+        try (FileInputStream descriptorStream = new FileInputStream(descriptorPath.toFile())) {
+            DescriptorProtos.FileDescriptorSet descriptorSet = DescriptorProtos.FileDescriptorSet.parseFrom(descriptorStream);
+            if (descriptorSet.getFileCount() == 0) {
+                throw new RuntimeException("Descriptor set is empty. Check your proto file.");
+            }
+            java.util.Map<String, Descriptors.FileDescriptor> built = new java.util.HashMap<>();
+            Descriptors.FileDescriptor targetFd = null;
+            for (DescriptorProtos.FileDescriptorProto fdp : descriptorSet.getFileList()) {
+                Descriptors.FileDescriptor[] deps = fdp.getDependencyList().stream()
+                        .map(built::get)
+                        .filter(java.util.Objects::nonNull)
+                        .toArray(Descriptors.FileDescriptor[]::new);
+                Descriptors.FileDescriptor fd = Descriptors.FileDescriptor.buildFrom(fdp, deps);
+                built.put(fd.getName(), fd);
+                if (fd.findServiceByName(serviceName) != null) {
+                    targetFd = fd;
+                }
+            }
+            if (targetFd == null) {
+                throw new RuntimeException("Service not found: " + serviceName);
+            }
+            Descriptors.ServiceDescriptor serviceDescriptor = targetFd.findServiceByName(serviceName);
+            Descriptors.MethodDescriptor methodDescriptor = serviceDescriptor.findMethodByName(methodName);
+            if (methodDescriptor == null) {
+                throw new RuntimeException("Method not found: " + methodName);
+            }
+            return methodDescriptor;
+        }
+    }

Add imports (outside this hunk):

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
🤖 Prompt for AI Agents
In
dynamic_grpc_client/src/main/java/com/testsigma/addons/ios/GrpcClientAction.java
around lines 139 to 160, the current implementation only uses
descriptorSet.getFile(0) and builds a FileDescriptor with no dependencies which
breaks when the proto imports types from other files; replace this by loading
all FileDescriptorProto entries from the descriptor set into a Map<String,
DescriptorProtos.FileDescriptorProto>, then iteratively (or topologically) build
Descriptors.FileDescriptor instances by resolving and passing the dependent
Descriptors.FileDescriptor[] for each file (using a cache/map of already-built
FileDescriptors) so imported types are available when you call
findServiceByName/findMethodByName; also add the recommended imports
(java.util.HashMap, java.util.Map, java.util.Objects).

Comment on lines +139 to +160
private Descriptors.MethodDescriptor loadServiceDescriptors(Path descriptorPath, String serviceName, String methodName) throws Exception {
try (FileInputStream descriptorStream = new FileInputStream(descriptorPath.toFile())) {
DescriptorProtos.FileDescriptorSet descriptorSet = DescriptorProtos.FileDescriptorSet.parseFrom(descriptorStream);
if (descriptorSet.getFileCount() == 0) {
throw new RuntimeException("Descriptor set is empty. Check your proto file.");
}
DescriptorProtos.FileDescriptorProto fileDescriptorProto = descriptorSet.getFile(0);

Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor.buildFrom(fileDescriptorProto, new Descriptors.FileDescriptor[]{});

Descriptors.ServiceDescriptor serviceDescriptor = fileDescriptor.findServiceByName(serviceName);
if (serviceDescriptor == null) {
throw new RuntimeException("Service not found: " + serviceName);
}

Descriptors.MethodDescriptor methodDescriptor = serviceDescriptor.findMethodByName(methodName);
if (methodDescriptor == null) {
throw new RuntimeException("Method not found: " + methodName);
}
return methodDescriptor;
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Descriptor loading is incorrect: only first file is used and dependencies are ignored (breaks protos with imports).

This will fail for most real-world protos. Build all FileDescriptors with dependencies and search across them.

Apply this diff to replace the method body:

-    private Descriptors.MethodDescriptor loadServiceDescriptors(Path descriptorPath, String serviceName, String methodName) throws Exception {
-        try (FileInputStream descriptorStream = new FileInputStream(descriptorPath.toFile())) {
-            DescriptorProtos.FileDescriptorSet descriptorSet = DescriptorProtos.FileDescriptorSet.parseFrom(descriptorStream);
-            if (descriptorSet.getFileCount() == 0) {
-                throw new RuntimeException("Descriptor set is empty. Check your proto file.");
-            }
-            DescriptorProtos.FileDescriptorProto fileDescriptorProto = descriptorSet.getFile(0);
-
-            Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor.buildFrom(fileDescriptorProto, new Descriptors.FileDescriptor[]{});
-
-            Descriptors.ServiceDescriptor serviceDescriptor = fileDescriptor.findServiceByName(serviceName);
-            if (serviceDescriptor == null) {
-                throw new RuntimeException("Service not found: " + serviceName);
-            }
-
-            Descriptors.MethodDescriptor methodDescriptor = serviceDescriptor.findMethodByName(methodName);
-            if (methodDescriptor == null) {
-                throw new RuntimeException("Method not found: " + methodName);
-            }
-            return methodDescriptor;
-        }
-    }
+    private Descriptors.MethodDescriptor loadServiceDescriptors(Path descriptorPath, String serviceName, String methodName) throws Exception {
+        try (FileInputStream descriptorStream = new FileInputStream(descriptorPath.toFile())) {
+            DescriptorProtos.FileDescriptorSet descriptorSet = DescriptorProtos.FileDescriptorSet.parseFrom(descriptorStream);
+            if (descriptorSet.getFileCount() == 0) {
+                throw new RuntimeException("Descriptor set is empty. Check your proto file.");
+            }
+
+            Map<String, DescriptorProtos.FileDescriptorProto> protoByName = new HashMap<>();
+            for (DescriptorProtos.FileDescriptorProto fdp : descriptorSet.getFileList()) {
+                protoByName.put(fdp.getName(), fdp);
+            }
+
+            Map<String, Descriptors.FileDescriptor> built = new HashMap<>();
+            for (DescriptorProtos.FileDescriptorProto fdp : descriptorSet.getFileList()) {
+                buildFileDescriptor(fdp.getName(), protoByName, built);
+            }
+
+            for (Descriptors.FileDescriptor fd : built.values()) {
+                Descriptors.ServiceDescriptor serviceDescriptor = fd.findServiceByName(serviceName);
+                if (serviceDescriptor != null) {
+                    Descriptors.MethodDescriptor methodDescriptor = serviceDescriptor.findMethodByName(methodName);
+                    if (methodDescriptor == null) {
+                        throw new RuntimeException("Method not found: " + methodName + " in service: " + serviceName);
+                    }
+                    return methodDescriptor;
+                }
+            }
+            throw new RuntimeException("Service not found: " + serviceName);
+        }
+    }

Add this helper and imports elsewhere in the class:

// imports to add
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;

// helper to add inside the class
private Descriptors.FileDescriptor buildFileDescriptor(
        String name,
        Map<String, DescriptorProtos.FileDescriptorProto> protoByName,
        Map<String, Descriptors.FileDescriptor> built) throws Descriptors.DescriptorValidationException {
    if (built.containsKey(name)) return built.get(name);
    DescriptorProtos.FileDescriptorProto fdp = protoByName.get(name);
    if (fdp == null) throw new RuntimeException("Missing descriptor proto for dependency: " + name);
    List<Descriptors.FileDescriptor> deps = new ArrayList<>();
    for (String depName : fdp.getDependencyList()) {
        deps.add(buildFileDescriptor(depName, protoByName, built));
    }
    Descriptors.FileDescriptor fd = Descriptors.FileDescriptor.buildFrom(
            fdp, deps.toArray(new Descriptors.FileDescriptor[0]));
    built.put(name, fd);
    return fd;
}
🤖 Prompt for AI Agents
In
dynamic_grpc_client/src/main/java/com/testsigma/addons/restapi/GrpcClientAction.java
around lines 139-160, the current loadServiceDescriptors reads only the first
FileDescriptorProto and ignores imports/dependencies which breaks protos with
dependencies; update the method to parse all FileDescriptorProto entries into a
map, add the provided recursive buildFileDescriptor helper and required imports
(Map, HashMap, List, ArrayList), use the helper to construct
Descriptors.FileDescriptor for each proto respecting dependencies, then search
across the built descriptors for the requested service and method (throwing the
existing RuntimeExceptions if not found), and replace the current method body
with this dependency-aware construction and lookup logic.

Comment on lines +174 to +178
private DynamicMessage performDynamicCall(ManagedChannel channel,
MethodDescriptor<DynamicMessage, DynamicMessage> method,
DynamicMessage request) {
return ClientCalls.blockingUnaryCall(channel, method, CallOptions.DEFAULT, request);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add a per-call deadline to prevent indefinite hangs.

Blocking without a deadline can stall the action/thread.

-        return ClientCalls.blockingUnaryCall(channel, method, CallOptions.DEFAULT, request);
+        return ClientCalls.blockingUnaryCall(
+                channel, method, CallOptions.DEFAULT.withDeadlineAfter(30, TimeUnit.SECONDS), request);
📝 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
private DynamicMessage performDynamicCall(ManagedChannel channel,
MethodDescriptor<DynamicMessage, DynamicMessage> method,
DynamicMessage request) {
return ClientCalls.blockingUnaryCall(channel, method, CallOptions.DEFAULT, request);
}
private DynamicMessage performDynamicCall(ManagedChannel channel,
MethodDescriptor<DynamicMessage, DynamicMessage> method,
DynamicMessage request) {
return ClientCalls.blockingUnaryCall(
channel, method, CallOptions.DEFAULT.withDeadlineAfter(30, TimeUnit.SECONDS), request);
}
🤖 Prompt for AI Agents
In
dynamic_grpc_client/src/main/java/com/testsigma/addons/restapi/GrpcClientAction.java
around lines 174 to 178, the blockingUnaryCall is invoked without a deadline
which can hang indefinitely; modify the call to use a CallOptions instance that
applies a per-call deadline (for example
CallOptions.DEFAULT.withDeadlineAfter(<timeout>, TimeUnit.<UNIT>)) and choose a
sensible default timeout (or make it configurable), then pass that CallOptions
to ClientCalls.blockingUnaryCall so the RPC will fail fast on timeout instead of
blocking forever.

Comment on lines +68 to +73
File protoFile = new File(protoFilePath.getValue().toString());
String protoDir = protoFile.getParent();
String protoFileName = protoFile.getName();

tempDescriptorFile = generateDescriptor(protoDir, protoFileName);

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Validate proto file and handle null parent path.

Same as iOS action.

-            File protoFile = new File(protoFilePath.getValue().toString());
-            String protoDir = protoFile.getParent();
+            File protoFile = new File(protoFilePath.getValue().toString());
+            if (!protoFile.exists()) {
+                throw new java.io.FileNotFoundException("Proto file not found: " + protoFile.getPath());
+            }
+            String protoDir = protoFile.getParent();
+            if (protoDir == null) {
+                protoDir = protoFile.getAbsoluteFile().getParent();
+            }
             String protoFileName = protoFile.getName();
📝 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
File protoFile = new File(protoFilePath.getValue().toString());
String protoDir = protoFile.getParent();
String protoFileName = protoFile.getName();
tempDescriptorFile = generateDescriptor(protoDir, protoFileName);
File protoFile = new File(protoFilePath.getValue().toString());
if (!protoFile.exists()) {
throw new java.io.FileNotFoundException("Proto file not found: " + protoFile.getPath());
}
String protoDir = protoFile.getParent();
if (protoDir == null) {
protoDir = protoFile.getAbsoluteFile().getParent();
}
String protoFileName = protoFile.getName();
tempDescriptorFile = generateDescriptor(protoDir, protoFileName);
🤖 Prompt for AI Agents
In
dynamic_grpc_client/src/main/java/com/testsigma/addons/web/GrpcClientAction.java
around lines 68 to 73, validate protoFilePath and the File object before using
getParent(): ensure protoFilePath.getValue() is non-null and points to an
existing file (throw or log a clear error if missing or not a file), compute
protoDir defensively by using protoFile.getParent() and if that returns null
fall back to protoFile.getAbsoluteFile().getParent() and finally to "." (or an
explicit temp directory) so you never pass a null directory to
generateDescriptor, and keep protoFileName = protoFile.getName() as before.

Comment on lines +139 to +160
private Descriptors.MethodDescriptor loadServiceDescriptors(Path descriptorPath, String serviceName, String methodName) throws Exception {
try (FileInputStream descriptorStream = new FileInputStream(descriptorPath.toFile())) {
DescriptorProtos.FileDescriptorSet descriptorSet = DescriptorProtos.FileDescriptorSet.parseFrom(descriptorStream);
if (descriptorSet.getFileCount() == 0) {
throw new RuntimeException("Descriptor set is empty. Check your proto file.");
}
DescriptorProtos.FileDescriptorProto fileDescriptorProto = descriptorSet.getFile(0);

Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor.buildFrom(fileDescriptorProto, new Descriptors.FileDescriptor[]{});

Descriptors.ServiceDescriptor serviceDescriptor = fileDescriptor.findServiceByName(serviceName);
if (serviceDescriptor == null) {
throw new RuntimeException("Service not found: " + serviceName);
}

Descriptors.MethodDescriptor methodDescriptor = serviceDescriptor.findMethodByName(methodName);
if (methodDescriptor == null) {
throw new RuntimeException("Method not found: " + methodName);
}
return methodDescriptor;
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Resolve descriptor dependencies; first-file-only approach is fragile.

Mirror the iOS fix to support imports.

Apply the same method body as suggested for the iOS file and add imports:

  • java.util.Map, HashMap, Objects

@@ -0,0 +1 @@
testsigma-sdk.api.key=eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIyMjMyMmM2Ni04NWYzLWIyN2UtN2FiOS0zM2U2M2Q4OWM1MGIiLCJ1bmlxdWVJZCI6IjUzOTkiLCJpZGVudGl0eUFjY291bnRVVUlkIjoiNDMifQ.ExHn2NqMjR0VelPGawCDpn9g530QTzrrmL4OnnGl26R76fqJcSWS6PFPWp9hQj4zLoxKxY3in-u8oBz_hTRKSQ No newline at end of file
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Remove committed JWT immediately; rotate the key.

A real JWT secret is committed under main/resources and will be shipped in the JAR. This is a critical secret leak and a compliance risk.

Apply this diff to remove the hardcoded secret (prefer deleting the file entirely or moving to src/test/resources with a placeholder; load via env/CI secrets instead):

-testsigma-sdk.api.key=eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIyMjMyMmM2Ni04NWYzLWIyN2UtN2FiOS0zM2U2M2Q4OWM1MGIiLCJ1bmlxdWVJZCI6IjUzOTkiLCJpZGVudGl0eUFjY291bnRVVUlkIjoiNDMifQ.ExHn2NqMjR0VelPGawCDpn9g530QTzrrmL4OnnGl26R76fqJcSWS6PFPWp9hQj4zLoxKxY3in-u8oBz_hTRKSQ

Run to locate other exposures:

Follow-ups:

  • Revoke/rotate the leaked token server-side.
  • Purge from git history if needed and add the file/path to .gitignore.
  • Use env vars or Maven filtering with CI-injected secrets; never commit real tokens.

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n -C2 -i 'testsigma-sdk\.api\.key|eyJhbGciOiJIUz' || true

Length of output: 45674


Remove all committed JWTs immediately — revoke/rotate the keys now.

Search output shows testsigma-sdk.api.key present in dozens of src/main/resources/testsigma-sdk.properties files across the repo (including dynamic_grpc_client/src/main/resources/testsigma-sdk.properties). These are live JWTs that will be packaged in JARs — critical secret leak.

Actions (urgent):

  • Remove the hardcoded keys from all src/main/resources/testsigma-sdk.properties (delete or move to src/test/resources and use a placeholder).
  • Revoke/rotate every leaked token on the issuing service immediately.
  • Purge the secrets from git history if any leaked token was active (git filter-repo or BFG) and coordinate with security/ops.
  • Stop committing secrets: load keys from env/CI secrets (Maven/Gradle filtering, Vault, GitHub/GitLab secrets) and add the files/paths to .gitignore.
  • Add automated secret scanning (pre-commit hook/CI) to prevent recurrence.

Find remaining occurrences:
rg -n -S 'testsigma-sdk.api.key|eyJhbGciOiJIUz'

🧰 Tools
🪛 Gitleaks (8.28.0)

[high] 1-1: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)

🤖 Prompt for AI Agents
dynamic_grpc_client/src/main/resources/testsigma-sdk.properties lines 1-1
contain a hardcoded JWT secret; remove this secret immediately from the file and
replace it with a non-secret placeholder (or move the file to src/test/resources
for tests), update code to load the real key from environment/CI secret
management (e.g., env var, Maven/Gradle filtered property, or Vault), add the
properties path to .gitignore to prevent future commits, coordinate with
security/ops to revoke/rotate the leaked token and purge it from git history
(git filter-repo or BFG), and enable automated secret scanning (pre-commit
hook/CI) to block similar commits going forward.

@akhil-testsigma akhil-testsigma merged commit 2e116ee into dev Sep 23, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants