Skip to content

Fix GCP Service Agent Token Theft vulnerability in connection testing - #1623

Open
akkaur wants to merge 1 commit into
developfrom
fix-vrp-service-agent-token-theft
Open

Fix GCP Service Agent Token Theft vulnerability in connection testing#1623
akkaur wants to merge 1 commit into
developfrom
fix-vrp-service-agent-token-theft

Conversation

@akkaur

@akkaur akkaur commented Sep 2, 2026

Copy link
Copy Markdown

Modifies GCPUtils.loadServiceAccountCredentials to use ServiceAccountCredentials.fromStream() instead of GoogleCredentials.fromStream(). Previously, GoogleCredentials.fromStream() would implicitly parse various credential types. This allowed a malicious user to supply an external_account payload with a crafted credential_source (pointing to the GCP metadata server) and an external token_url, triggering an unintended Workload Identity Federation exchange and exfiltrating the Data Fusion Service Agent's token. By explicitly restricting the parser to ServiceAccountCredentials, external and non-service-account configurations now instantly throw an IOException prior to any external token fetches and effectively blocks the Privilege Escalation attack.

Also adds GCPUtilsTest.java to explicitly test that only service_account credential types can be loaded, securing regression coverage against external account injections.

@akkaur
akkaur requested a review from sahusanket September 2, 2026 18:16
@akkaur
akkaur marked this pull request as ready for review September 2, 2026 18:17
@akkaur akkaur self-assigned this Sep 2, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request updates GCPUtils.loadServiceAccountCredentials to load credentials using ServiceAccountCredentials.fromStream instead of GoogleCredentials.fromStream and adds corresponding unit tests. The feedback suggests enhancing the positive test case by asserting that the returned credentials are an instance of ServiceAccountCredentials to ensure the security fix is explicitly verified.

Comment thread src/test/java/io/cdap/plugin/gcp/common/GCPUtilsTest.java
@akkaur
akkaur force-pushed the fix-vrp-service-agent-token-theft branch 3 times, most recently from 04bc968 to 354a90a Compare September 2, 2026 18:26
@akkaur

akkaur commented Sep 2, 2026

Copy link
Copy Markdown
Author

/gemini review

1 similar comment
@akkaur

akkaur commented Sep 2, 2026

Copy link
Copy Markdown
Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request updates the loadServiceAccountCredentials method in GCPUtils to return ServiceAccountCredentials instead of GoogleCredentials, preventing the loading of non-service-account credentials. A new test class GCPUtilsTest has been added to validate this behavior with both positive and negative test cases. I have no feedback to provide.

@gemini-code-assist

Copy link
Copy Markdown

The changes in this pull request correctly address the identified security vulnerability by restricting credential parsing to ServiceAccountCredentials. This prevents the use of malicious external_account payloads that could lead to token exfiltration. The addition of GCPUtilsTest.java provides essential regression testing to ensure this restriction remains in place. The implementation is robust and follows security best practices for GCP credential handling.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request updates GCPUtils to load credentials specifically as ServiceAccountCredentials instead of the generic GoogleCredentials to mitigate potential external account payload attacks, and introduces unit tests to verify this behavior. The review feedback suggests programmatically generating a dummy RSA keypair at runtime in the test instead of hardcoding a static private key string, which prevents triggering automated credential scanners in CI/CD pipelines.

Comment thread src/test/java/io/cdap/plugin/gcp/common/GCPUtilsTest.java
… and plugin validation

Modified GCPUtils.loadServiceAccountCredentials to use ServiceAccountCredentials.fromStream() instead of GoogleCredentials.fromStream(). Previously, GoogleCredentials.fromStream() would implicitly parse various credential types. This allowed a malicious user to supply an external_account payload with a crafted credential_source (pointing to the GCP metadata server) and an external token_url, triggering an unintended Workload Identity Federation exchange and exfiltrating the Data Fusion Service Agent's token. By explicitly restricting the parser to ServiceAccountCredentials, external and non-service-account configurations now instantly throw an IOException prior to any external token fetches and effectively blocks the Privilege Escalation attack. Also added GCPUtilsTest.java to explicitly test that only service_account credential types can be loaded, securing regression coverage against external account injections. Fixes: b/501546932, b/501543027
@akkaur
akkaur force-pushed the fix-vrp-service-agent-token-theft branch from 354a90a to e1c02fe Compare September 2, 2026 18:42
@akkaur

akkaur commented Sep 2, 2026

Copy link
Copy Markdown
Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request updates GCPUtils.loadServiceAccountCredentials to load credentials using ServiceAccountCredentials.fromStream instead of the generic GoogleCredentials.fromStream to prevent loading arbitrary external account credentials. Additionally, a new test class GCPUtilsTest has been added to verify both positive and negative scenarios for loading service account credentials. No review comments were provided, so there is no feedback to address.

throws IOException {
try (InputStream inputStream = openServiceAccount(serviceAccount, isServiceAccountFilePath)) {
return GoogleCredentials.fromStream(inputStream);
return ServiceAccountCredentials.fromStream(inputStream);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Changing from GoogleCredentials.fromStream() to ServiceAccountCredentials.fromStream() restricts your application to accepting only traditional, long-lived service account key JSON files.

The primary impacts across your application include:

  • Breaks Workload Identity Federation (external_account): Workloads authenticating from AWS, Azure, GitHub Actions, or on-premises environments via keyless federation will crash with an IOException or IllegalArgumentException because an external_account JSON cannot be parsed by ServiceAccountCredentials.
  • Breaks Local Developer Workflows (authorized_user): Developers using credential files generated by gcloud auth application-default login or user OAuth flows will no longer be able to supply those files to your code.
  • Blocks Impersonated Account Files (impersonated_service_account): Configuration files designed to dynamically impersonate a service account without possessing its private key will fail to load.
  • Enforces Security Anti-Patterns: It forces consumers of your application or plugin to create, download, store, and rotate static service account private keys ("type": "service_account"), increasing the risk of leaked credentials.
  • API Incompatibility / Code Changes: Any calling code expecting the base GoogleCredentials type may need adjustments if it relies on polymorphic credential handling, though ServiceAccountCredentials is assignable to GoogleCredentials.
  • Gains Direct Access to Service Account Methods: On the positive side, you gain compile-time access to service account-specific methods on the returned object without casting—such as .createDelegated(userEmail) for Google Workspace Domain-Wide Delegation, .getClientEmail(), and .getPrivateKey().

Would it be fine?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

To remediate you may consider implementing stricter input validation for Google Cloud connector configurations.

Example:

  • Implement Stricter Input Validation: Add validation checks in the backend handlers (such as ConnectionHandler.java in CDAP and individual plugin connectors like GCSConnector.java) for the serviceAccountJSON payload.
  • Restrict Credential Type Properties: Do not allow the credential_source URL to point to the local metadata server (http://metadata.google.internal/computeMetadata/v1/...) or internal loopback IP addresses when testing connections with external accounts.
  • Validate Token URLs: Allowlist or validate the token_url domain to ensure it points to legitimate identity providers, rather than arbitrary attacker-controlled destinations.
  • Audit All Google Connectors: Ensure the validation applies systemically across all Google Cloud connectors (BigQuery, GCS, Spanner, Bigtable, Pub/Sub, etc.) as the vulnerability may be present in multiple plugin configurations.

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.

2 participants