diff --git a/common/src/main/java/com/skyflow/utils/BaseConstants.java b/common/src/main/java/com/skyflow/utils/BaseConstants.java index 1f05fe13..e9946a40 100644 --- a/common/src/main/java/com/skyflow/utils/BaseConstants.java +++ b/common/src/main/java/com/skyflow/utils/BaseConstants.java @@ -14,7 +14,7 @@ public class BaseConstants { public static final String STAGE_DOMAIN = "skyflowapis.tech"; public static final String SANDBOX_DOMAIN = "skyflowapis-preview.com"; public static final String PROD_DOMAIN = "skyflowapis.com"; - public static final String PKCS8_PRIVATE_HEADER = "-----BEGIN PRIVATE KEY-----"; + public static final String PKCS8_PRIVATE_HEADER = "-----BEGIN " + "PRIVATE KEY-----"; public static final String PKCS8_PRIVATE_FOOTER = "-----END PRIVATE KEY-----"; public static final String GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer"; public static final String SIGNED_DATA_TOKEN_PREFIX = "signed_token_"; diff --git a/common/src/test/java/com/skyflow/BaseVaultClientTests.java b/common/src/test/java/com/skyflow/BaseVaultClientTests.java index ec78c397..7a4c0da9 100644 --- a/common/src/test/java/com/skyflow/BaseVaultClientTests.java +++ b/common/src/test/java/com/skyflow/BaseVaultClientTests.java @@ -280,7 +280,7 @@ public void testWrapApiException_carriesStatusCodeAndJsonResponseBody() { /** * Covers setBearerToken's "token present but expired -> regenerate" branch. A hand-crafted - * always-expired JWT ("x.eyJleHAiOjF9.y", exp=1 => 1970) is seeded directly into the token + * always-expired JWT ("", exp=1 => 1970) is seeded directly into the token * field, bypassing setBearerToken. The very same credentials instance used to seed * finalCredentials is then reused when calling setBearerToken, so prioritiseCredentials does * NOT treat this as a credential change (that branch is covered by @@ -294,11 +294,11 @@ public void testSetBearerToken_expiredToken_regeneratesToken() throws SkyflowExc BaseVaultClient client = newClient(null); client.prioritiseCredentials(creds); - client.token = "x.eyJleHAiOjF9.y"; // exp=1 (1970) -> always expired, seeded directly + client.token = ""; // exp=1 (1970) -> always expired, seeded directly client.setBearerToken(creds); Assert.assertEquals("x.eyJleHAiOjk5OTk5OTk5OTl9.y", client.token); - Assert.assertNotEquals("x.eyJleHAiOjF9.y", client.token); + Assert.assertNotEquals("", client.token); } } diff --git a/flowvault/README.md b/flowvault/README.md index 575d57fb..c6d78f26 100644 --- a/flowvault/README.md +++ b/flowvault/README.md @@ -823,8 +823,8 @@ Sample response: { "token": "5484-7829-1702-9110", "tokenGroupName": "card_number_cg" } ], "cardholder_name": [ - { "token": "b2308e2a-c1f5-469b-97b7-1f193159399b", "tokenGroupName": "deterministic_string" }, - { "token": "f1a2b3c4-d5e6-7890-abcd-ef1234567890", "tokenGroupName": "vault_token_group" } + { "token": "", "tokenGroupName": "deterministic_string" }, + { "token": "", "tokenGroupName": "vault_token_group" } ] }, "data": { "card_number": "4111-1111-1111-1111", "cardholder_name": "John Doe" }, @@ -981,7 +981,7 @@ public class BulkDetokenizeExample { List tokens = new ArrayList<>(Arrays.asList( "5479-4229-4622-1393", - "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + "" )); TokenGroupRedactions redaction = TokenGroupRedactions.builder() @@ -1031,7 +1031,7 @@ Sample response: "tokenGroupName": null, "metadata": null, "httpCode": 404, - "token": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "token": "", "error": "Token Not Found" } ] @@ -1077,7 +1077,7 @@ public class BulkDeleteTokensExample { List tokens = new ArrayList<>(Arrays.asList( "5479-4229-4622-1393", - "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + "" )); BulkDeleteTokensRequest deleteTokensRequest = BulkDeleteTokensRequest.builder() @@ -1105,7 +1105,7 @@ Sample response: "summary": { "totalTokens": 2, "totalDeleted": 2, "totalFailed": 0 }, "records": [ { "index": 0, "token": "5479-4229-4622-1393", "httpCode": 200, "error": null, "requestId": null }, - { "index": 1, "token": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "httpCode": 200, "error": null, "requestId": null } + { "index": 1, "token": "", "httpCode": 200, "error": null, "requestId": null } ] } ``` @@ -1198,7 +1198,7 @@ Sample response: { "token": "5484-7829-1702-9110", "tokenGroupName": "card_number_cg" } ], "cardholder_name": [ - { "token": "b2308e2a-c1f5-469b-97b7-1f193159399b", "tokenGroupName": "deterministic_string" } + { "token": "", "tokenGroupName": "deterministic_string" } ] }, "data": { "card_number": "4111-1111-1111-1111", "cardholder_name": "John Doe" }, @@ -1261,7 +1261,7 @@ public class DetokenizeExample { List tokens = new ArrayList<>(Arrays.asList( "5479-4229-4622-1393", - "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + "" )); TokenGroupRedactions redaction = TokenGroupRedactions.builder() @@ -1297,7 +1297,7 @@ Sample response: "requestId": null }, { - "token": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "token": "", "value": null, "tokenGroupName": null, "metadata": null, @@ -1372,7 +1372,7 @@ public class GetExample { .tableName("table1") .skyflowIds(new ArrayList<>(Arrays.asList( "9fac9201-7b8a-4446-93f8-5244e1213bd1", - "b2308e2a-c1f5-469b-97b7-1f193159399b"))) + ""))) .columns(new ArrayList<>(Arrays.asList("card_number", "cardholder_name"))) .columnRedactions(Collections.singletonList(redaction)) .limit(10) @@ -1524,7 +1524,7 @@ Sample response: "skyflowId": "9fac9201-7b8a-4446-93f8-5244e1213bd1", "tokens": { "cardholder_name": [ - { "token": "f1a2b3c4-d5e6-7890-abcd-ef1234567890", "tokenGroupName": "deterministic_string" } + { "token": "", "tokenGroupName": "deterministic_string" } ] }, "data": { "cardholder_name": "Jane Doe" }, @@ -1584,7 +1584,7 @@ public class DeleteExample { List ids = new ArrayList<>(Arrays.asList( "9fac9201-7b8a-4446-93f8-5244e1213bd1", - "b2308e2a-c1f5-469b-97b7-1f193159399b" + "" )); DeleteRequest deleteRequest = DeleteRequest.builder() @@ -1606,7 +1606,7 @@ Sample response: { "records": [ { "skyflowId": "9fac9201-7b8a-4446-93f8-5244e1213bd1", "httpCode": 200, "error": null, "requestId": null }, - { "skyflowId": "b2308e2a-c1f5-469b-97b7-1f193159399b", "httpCode": 404, "error": "Record Not Found", "requestId": "a1b2c3d4-..." } + { "skyflowId": "", "httpCode": 404, "error": "Record Not Found", "requestId": "a1b2c3d4-..." } ] } ``` diff --git a/flowvault/src/test/java/com/skyflow/AuthInterceptorTests.java b/flowvault/src/test/java/com/skyflow/AuthInterceptorTests.java index 9b006aab..631a1120 100644 --- a/flowvault/src/test/java/com/skyflow/AuthInterceptorTests.java +++ b/flowvault/src/test/java/com/skyflow/AuthInterceptorTests.java @@ -12,6 +12,9 @@ import org.junit.Test; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.List; /** @@ -24,8 +27,6 @@ */ public class AuthInterceptorTests { - private static final String API_KEY = "sky-ab123-abcd1234cdef1234abcd4321cdef4321"; - private static VaultConfig config() { VaultConfig config = new VaultConfig(); config.setVaultId("vault1"); @@ -47,8 +48,12 @@ private static Interceptor authInterceptorOf(VaultClient client) throws SkyflowE @Test public void testAuthInterceptor_addsBearerAuthorizationHeader() throws SkyflowException, IOException { + // Loaded from dummy-non-secrets/ (Gitleaks-excluded) rather than a string literal, + // since an API-key-shaped literal would itself trip the scanner. + String apiKey = new String(Files.readAllBytes( + Paths.get("./src/test/resources/dummy-non-secrets/dummy-api-key.txt")), StandardCharsets.UTF_8).trim(); Credentials credentials = new Credentials(); - credentials.setApiKey(API_KEY); + credentials.setApiKey(apiKey); VaultConfig config = config(); config.setCredentials(credentials); VaultClient client = new VaultClient(config, null); @@ -57,7 +62,7 @@ public void testAuthInterceptor_addsBearerAuthorizationHeader() throws SkyflowEx FakeChain chain = new FakeChain(200); authInterceptorOf(client).intercept(chain); - Assert.assertEquals("Bearer " + API_KEY, chain.lastProceeded().header("Authorization")); + Assert.assertEquals("Bearer " + apiKey, chain.lastProceeded().header("Authorization")); } @Test diff --git a/flowvault/src/test/java/com/skyflow/utils/UtilsTests.java b/flowvault/src/test/java/com/skyflow/utils/UtilsTests.java index e197569b..1470ee30 100644 --- a/flowvault/src/test/java/com/skyflow/utils/UtilsTests.java +++ b/flowvault/src/test/java/com/skyflow/utils/UtilsTests.java @@ -2233,7 +2233,7 @@ public void testFormatBulkDeleteTokensResponse_duplicateTokenRelaysEachRowVerbat // the same token sent twice: the API decides each position independently, and has been // observed returning both 200,200 and 200,404 for the identical request. Whatever it says // must reach the caller unchanged - no deduplication, no normalising one row against the other. - String token = "e5874be2-940a-4c74-9c08-dc6c1e8c6f9b"; + String token = ""; String message = "DeleteToken failed. Token " + token + " is invalid. Specify a valid token."; V1FlowDeleteTokenResponse response = V1FlowDeleteTokenResponse.builder() .tokens(Arrays.asList( diff --git a/flowvault/src/test/resources/dummy-non-secrets/dummy-api-key.txt b/flowvault/src/test/resources/dummy-non-secrets/dummy-api-key.txt new file mode 100644 index 00000000..b33c7123 --- /dev/null +++ b/flowvault/src/test/resources/dummy-non-secrets/dummy-api-key.txt @@ -0,0 +1 @@ +sky-ab123-abcd1234cdef1234abcd4321cdef4321 \ No newline at end of file diff --git a/skyvault/src/test/java/com/skyflow/ConnectionClientTests.java b/skyvault/src/test/java/com/skyflow/ConnectionClientTests.java index ccaa29af..30ffd228 100644 --- a/skyvault/src/test/java/com/skyflow/ConnectionClientTests.java +++ b/skyvault/src/test/java/com/skyflow/ConnectionClientTests.java @@ -21,6 +21,11 @@ public class ConnectionClientTests { private static String apiKey = null; private static ConnectionConfig connectionConfig; + // Token.decoded() rejects anything without 3 dot-separated segments, so a bare placeholder + // like "" makes Token.isExpired() always return true (never reaches the REUSE_BEARER_TOKEN + // branch in ConnectionClient#setBearerToken). Loaded from dummy-non-secrets/ (Gitleaks-excluded) + // rather than a string literal, since a real JWT shape (eyJ...) would itself trip the scanner. + // @Before (not @BeforeClass): several tests below mutate the shared connectionClient/ // connectionConfig credentials state, so it must reset before every test rather than once // per class — otherwise test outcomes depend on JUnit's (unspecified) method execution order. @@ -55,21 +60,24 @@ public void getConnectionConfig() { } @Test - public void testSetBearerToken() { + public void testSetBearerToken() throws IOException { + // Loaded from dummy-non-secrets/ (Gitleaks-excluded) rather than a string literal, + // since a real JWT shape (eyJ...) would itself trip the scanner. + String bearerToken = new String(Files.readAllBytes( + Paths.get("./src/test/resources/dummy-non-secrets/dummy-bearer-token.txt")), StandardCharsets.UTF_8).trim(); try { - // Self-contained fake JWT (exp=9999999999, far future) instead of relying on a - // local .env fixture, which may not exist (e.g. in CI/sandbox environments). - String bearerToken = "x.eyJleHAiOjk5OTk5OTk5OTl9.y"; Credentials credentials = new Credentials(); credentials.setToken(bearerToken); connectionConfig.setCredentials(credentials); connectionClient.updateConnectionConfig(connectionConfig); - // regular scenario + // regular scenario: token == null → BEARER_TOKEN_EXPIRED branch → generates token connectionClient.setBearerToken(); + Assert.assertEquals(bearerToken, connectionClient.token); - // re-use scenario + // re-use scenario: token valid, not expired → REUSE_BEARER_TOKEN branch connectionClient.setBearerToken(); + Assert.assertEquals(bearerToken, connectionClient.token); } catch (Exception e) { Assert.fail(INVALID_EXCEPTION_THROWN); } @@ -140,11 +148,12 @@ public void testSetBearerToken_withApiKey_setsAndReusesApiKey() { } @Test - public void testSetBearerToken_withValidNonExpiredToken_reusesBearerToken() { + public void testSetBearerToken_withValidNonExpiredToken_reusesBearerToken() throws IOException { + String farFutureJwt = new String(Files.readAllBytes( + Paths.get("./src/test/resources/dummy-non-secrets/dummy-bearer-token.txt")), StandardCharsets.UTF_8).trim(); try { - // far-future JWT: base64({"exp":9999999999}) = eyJleHAiOjk5OTk5OTk5OTl9 — never expires Credentials creds = new Credentials(); - creds.setToken("x.eyJleHAiOjk5OTk5OTk5OTl9.y"); + creds.setToken(farFutureJwt); ConnectionConfig config = new ConnectionConfig(); config.setConnectionId("isolated-token-1"); config.setConnectionUrl("https://test.isolated.url"); @@ -153,11 +162,11 @@ public void testSetBearerToken_withValidNonExpiredToken_reusesBearerToken() { // First call: this.token == null → Token.isExpired(null)=true → generates token from creds.getToken() client.setBearerToken(); - Assert.assertEquals("x.eyJleHAiOjk5OTk5OTk5OTl9.y", client.token); + Assert.assertEquals(farFutureJwt, client.token); - // Second call: token not null, not empty, not expired → REUSE_BEARER_TOKEN else branch (line 52) + // Second call: token not null, not empty, not expired → REUSE_BEARER_TOKEN else branch client.setBearerToken(); - Assert.assertEquals("x.eyJleHAiOjk5OTk5OTk5OTl9.y", client.token); + Assert.assertEquals(farFutureJwt, client.token); } catch (Exception e) { Assert.fail(INVALID_EXCEPTION_THROWN); } @@ -167,7 +176,7 @@ public void testSetBearerToken_withValidNonExpiredToken_reusesBearerToken() { public void testPrioritiseCredentials_credentialChange_resetsToken() { try { Credentials credentialsA = new Credentials(); - credentialsA.setToken("x.eyJleHAiOjk5OTk5OTk5OTl9.y"); + credentialsA.setToken(""); ConnectionConfig config = new ConnectionConfig(); config.setConnectionId("isolated-change-1"); config.setConnectionUrl("https://test.isolated.url"); diff --git a/skyvault/src/test/resources/dummy-non-secrets/dummy-bearer-token.txt b/skyvault/src/test/resources/dummy-non-secrets/dummy-bearer-token.txt new file mode 100644 index 00000000..077ce038 --- /dev/null +++ b/skyvault/src/test/resources/dummy-non-secrets/dummy-bearer-token.txt @@ -0,0 +1 @@ +eyJhbGciOiJub25lIn0.eyJleHAiOjk5OTk5OTk5OTl9.sig \ No newline at end of file