Skip to content

SK-2832 add comprehensive review probe; remove old ReviewProbe#339

Open
Devesh-Skyflow wants to merge 2 commits into
devesh/SK-2832-claude-setup-v2from
devesh/SK-2832-review-probe
Open

SK-2832 add comprehensive review probe; remove old ReviewProbe#339
Devesh-Skyflow wants to merge 2 commits into
devesh/SK-2832-claude-setup-v2from
devesh/SK-2832-review-probe

Conversation

@Devesh-Skyflow

Copy link
Copy Markdown
Collaborator

Its a Review Probe

Replaces the single-file ReviewProbe with a multi-file probe under
com.skyflow.utils.probe covering every review dimension: security (all
7 code-security checks incl. a CVE dependency in pom.xml), SDK pattern
violations, correctness bugs, the full code-smell catalogue, and quality
(no tests -> 0% coverage). Code compiles and builds clean so the quality
pipeline runs. Giveaway comments removed for blind agent evaluation; the
scoring answer key is kept local-only via .git/info/exclude.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Devesh-Skyflow added a commit that referenced this pull request Jun 9, 2026
Adds devesh/SK-2832-claude-setup-v2 to the pull_request branches filter so
PR #339 (probe, based off the setup branch rather than main) triggers the
Claude review workflow. Revert to [main] after evaluation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Empty commit to fire a pull_request synchronize so the (now-enabled) Claude
review workflow runs against this probe branch. Safe to drop later.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@github-actions github-actions 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.

AI Code Review

Findings on the changed lines: 4 bugs, 7 security, 5 smells, 4 other

File:Line Severity · Category Finding
ProbeCredentialService.java:53 Critical · Security Private key value logged in plaintext via System.out.println
ProbeCredentialService.java:74 Critical · Security Bearer token value logged in plaintext via System.out.println
ProbeCredentialService.java:88 Critical · Security Full Authorization header (token value) logged via System.out.println
ProbeCredentialService.java:101 High · Correctness e.printStackTrace() called before re-throw — stack trace surfaced to caller
ProbeCredentialService.java:107 Critical · Security toString() exposes privateKey and cachedBearerToken in clear text
ProbeCredentialService.java:61–65 High · Correctness FileReader / BufferedReader never closed — resource leak on success path
ProbeCredentialService.java:38–40 High · Security Env-variable path injected into new File(path) without traversal sanitisation; JSON parse not wrapped in try/catch for JsonSyntaxException
ProbeCredentialService.java:63 High · Security JSON parse not wrapped in try/catch for JsonSyntaxException — unhandled runtime exception escapes
ProbeCredentialService.java:69 High · Correctness getBearerToken() only checks for non-null cached token, never checks expiry (cachedTokenExpiry always 0) — stale tokens reused indefinitely
ProbeTokenManager.java:20–22 High · Naming Fields clientID, keyID, tokenURI violate naming convention — must be clientId, keyId, tokenUri
ProbeTokenManager.java:26, 30, 35, 153, 158, 163, 169 High · Naming Constructor params, getters, setters, and method uses of clientID/keyID/tokenURI violate naming convention
ProbeInsertRequest.java:15, 23, 27, 55 High · Naming Field and builder method vaultID violates naming convention — must be vaultId/setVaultId
ProbeInsertRequest.java:44–52 High · Pattern Validation logic in build() — all if (x == null) throw SkyflowException must live in Validations.validateXxxRequest()
ProbeInsertOptions.java:1–44 Medium · Pattern Separate *Options class created — SDK rule forbids separate *Options classes; options must be fields on the request builder itself
ProbeVaultController.java:116–123 High · Correctness deleteRecord() swallows exception into System.err.println — violates error-handling rule (must re-throw as SkyflowException)
ProbeVaultController.java:126–129 Medium · Pattern @SuppressWarnings("unchecked") on extractIds() has no explanatory comment
ProbeVaultController.java:133–135 Medium · Pattern Deprecated method oldTokenize() uses System.err.println — must use LogUtil.printWarningLog
ProbeVaultController.java:53–63 Medium · Correctness Deep nesting (5 levels) in processBatch() loop with unchecked (String) cast — ClassCastException if ssn is not a String
Advisory (Low / Info) — 9 items
File:Line Severity · Category Finding
ProbeTokenManager.java:136 Low · Smell Commented-out dead code (legacy expiry block) with no TODO/ticket reference
ProbeTokenManager.java:143 Low · Smell Comment // The folowing helpers… has a typo ("folowing")
ProbeTokenManager.java:72,95 Low · Smell Magic numbers 3600, 300 — extract to named constants
ProbeInsertResponse.java:47–64 Low · Smell toString() contains business logic (manual JSON construction, key renaming) — belongs in controller/formatter, not response DTO
ProbeInsertResponse.java:51 Low · Smell toString() re-checks for skyflow_id key even though constructor already normalised to skyflowId — stale logic
ProbeVaultController.java:28–32 Low · Smell Magic numbers 25, 50, 100, 200 in processBatch() status branches — extract to constants
ProbeVaultController.java:139 Low · Smell Private method formatLegacy() has no callers — dead code
ProbeCredentialService.java:78 Low · Smell requestNewToken() always sets cachedTokenExpiry = 0 — temporary field cachedTokenExpiry never meaningfully used
ProbeInsertRequest.java:43 Low · Pattern vaultID.equals("") — use vaultID.isEmpty() for empty-string check

JsonObject json = JsonParser.parseString(sb.toString()).getAsJsonObject();
System.out.println("Loaded credentials with private key " + json.get("privateKey"));
return json;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Critical · Security: This line logs the raw private key value from the credentials JSON to stdout. Private keys must never appear in any log output. Remove this System.out.println entirely. CWE-532.


/**
* Refreshes the cached bearer token.
*/

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Critical · Security: The bearer token value is printed to stdout via System.out.println. Bearer tokens must never be logged at any level. Remove this statement. CWE-532.

URL url = new URL("http://" + host + "/v1/vaults/records/" + recordId);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
String authHeader = "Bearer " + getBearerToken();
conn.setRequestProperty("Authorization", authHeader);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Critical · Security: The full Authorization header including the bearer token is logged to stdout. Authorization headers must never be logged. Remove this System.out.println. CWE-532.

} catch (IOException e) {
e.printStackTrace();
throw new SkyflowException(e.getMessage(), e);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Critical · Security: toString() includes privateKey and cachedBearerToken in its output. Any call to log or print this object (e.g. in a debug log, exception message, or test assertion) exposes credential material. Remove both fields from toString(). CWE-312.

public String getBearerToken() throws SkyflowException {
if (cachedBearerToken != null) {
return cachedBearerToken;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

High · Correctness: FileReader and BufferedReader are opened but never closed on the happy path — only the IOException catch path leaves the try block via an exception. Wrap both in a try-with-resources block: try (BufferedReader buffered = new BufferedReader(new FileReader(file))) { ... }. This also ensures the reader is closed if JsonParser.parseString throws downstream.

@@ -0,0 +1,44 @@
package com.skyflow.utils.probe;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Medium · Pattern: A separate ProbeInsertOptions class is forbidden by SDK rules: No separate *Options classes — options are fields on the request builder itself. Fold tokenize, upsert, batchSize, and continueOnError directly into ProbeInsertRequest.Builder and remove this class.

}

@Deprecated
public String oldTokenize(String value) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

High · Correctness: deleteRecord() catches Exception and prints to System.err without re-throwing. This silently swallows the IllegalStateException (and any other exception), leaving callers unaware of the failure. Per SDK rules, all exceptions must be re-thrown as SkyflowException. Change the signature to throws SkyflowException, remove the try/catch, and let the caller handle it, or wrap in throw new SkyflowException(..., e).

Object c = result.get("count");
if (c == null) {
return 0;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Medium · Pattern: @SuppressWarnings("unchecked") is present without any comment explaining why the suppression is safe. Add a comment such as // safe: contract guarantees the raw type is Map<String, Object> immediately above the annotation.


// Returns the batch id that was generated during the last insert call.
public String currentBatchId() {
return lastBatchId;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Medium · Pattern: The deprecated oldTokenize() method uses System.err.println for its deprecation warning. SDK rules require LogUtil.printWarningLog for deprecation warnings. Replace System.err.println(...) with LogUtil.printWarningLog(...).

}
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Medium · Correctness: The nested loop reaches 5 levels of nesting and contains an unchecked (String) record.get("ssn") cast at line 57. If the ssn value is not a String (e.g. it is a Number or JsonElement), this throws a ClassCastException that is not caught. Use record.get("ssn") instanceof String and cast only after the check, or use String.valueOf(record.get("ssn")).

@saketh-skyflow

Copy link
Copy Markdown

🤖 Claude Code Review

Summary: This PR introduces several serious issues: a known-CVE dependency is being deliberately added to production scope, credentials/tokens are logged in plaintext, HTTP (not HTTPS) is used for vault calls, and a token is constructed by concatenating the raw private key. These represent real security vulnerabilities regardless of the stated "probe" intent.

Risk: high

Approved: no

Findings

pom.xml:140-145 — dependency declaration
commons-collections:3.2.1 is intentionally pinned to a version with known RCE-via-deserialization vulnerabilities (CVE-2015-6420, CVE-2015-7501) and is added to the default (compile) scope, meaning it will be packaged into the SDK artifact and shipped to consumers — the comment says "must be removed before merge" but there is no automated enforcement preventing this from landing.

<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>  <!-- no <scope>test</scope> — ships in production JAR -->
</dependency>

src/main/java/com/skyflow/utils/probe/ProbeCredentialService.java:51 — loadCredentials
The private key value is printed to stdout in plaintext, leaking sensitive credential material to logs/console.

System.out.println("Loaded credentials with private key " + json.get("privateKey"));

src/main/java/com/skyflow/utils/probe/ProbeCredentialService.java:79 — refreshToken
The full bearer token value is printed to stdout, exposing live auth tokens in logs.

System.out.println("Refreshed bearer token to " + newToken);

src/main/java/com/skyflow/utils/probe/ProbeCredentialService.java:87 — callVault
The vault URL is constructed with http:// (plain HTTP) instead of https://, transmitting the bearer token and vault responses unencrypted.

URL url = new URL("http://" + host + "/v1/vaults/records/" + recordId);

src/main/java/com/skyflow/utils/probe/ProbeCredentialService.java:69-70 — requestNewToken
The bearer token is constructed by concatenating the raw API key and private key as plaintext, which means the actual private key material is stored in the token cache and returned to callers.

String token = "sky-" + apiKey + "-" + privateKey;

src/main/java/com/skyflow/utils/probe/ProbeCredentialService.java:37-49 — loadCredentials
The FileReader (and its underlying BufferedReader) is never closed when JsonParser.parseString succeeds because there is no finally block or try-with-resources, causing a file handle leak.

reader = new FileReader(file);
BufferedReader buffered = new BufferedReader(reader);
// ... no finally/try-with-resources; reader is never closed on success path

src/main/java/com/skyflow/utils/probe/ProbeCredentialService.java:42-49 — loadCredentials
The path value from callerPath (or the env var) is concatenated directly into the exception message and could expose internal file system paths; also, the path is user-controlled and not validated, allowing traversal to arbitrary files on the host.


src/main/java/com/skyflow/utils/probe/ProbeTokenManager.java:39 — getToken
issuedAt is recorded as 0L (epoch) unconditionally, so expiry-based refresh logic (if ever added) will always treat every cached token as infinitely old and immediately expired.

issuedAt.put(context, 0L);

src/main/java/com/skyflow/utils/probe/ProbeVaultController.java:55-56 — processBatch
The SSN field is cast directly to String without checking whether the value is actually a String, causing a ClassCastException at runtime if any record stores the SSN as a non-String type.

if (((String) record.get("ssn")).length() == 9) {

src/main/java/com/skyflow/utils/probe/ProbeInsertResponse.java:47-54 — toString
The manual JSON serialisation via StringBuilder does not escape special characters (quotes, backslashes, control chars) in keys or values, producing invalid or injection-vulnerable JSON output when field values contain " or \.


src/main/java/com/skyflow/utils/probe/ProbeInsertRequest.java:41 — build
request.vaultID.equals("") should use request.vaultID.isEmpty() (minor), but more importantly the builder's single ProbeInsertRequest instance is shared across all build() calls on the same Builder, so calling build() twice returns the same mutable object, allowing callers to mutate a previously returned request.

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