clients/java: apply audit findings — true streaming upload + token redaction (0d3ee26 → next)
MEDIUM: - C1: multipart upload now actually streams via SequenceInputStream + Files.newInputStream. Code comment + README + javadoc updated to match reality. Test added uploading 10 MiB file with received-bytes assertion bounding envelope overhead. - S1: AppToken.toString() override redacts token (was leaking plaintext via record auto-toString). LOW: - C2: RunResult.result null/missing-field handling — canonical-constructor coerces null/NullNode to MissingNode, javadoc updated. - C3: HTTP timeout lower bound: Math.max(5L, n + 30L). - C4: ForgeClient implements AutoCloseable (no-op on JDK 17, documented). - S4: javadoc warning on uploadFile path traversal / symlink follow. Quality: - Q1: package-info.java added for com.clawdforge.exception (clears pom.xml dead exclude). - C7: @JsonInclude(NON_DEFAULT) on POST DTOs (drops wire "created_at": 0). Deps: - jackson-databind/core/annotations 2.17.2 → 2.18.2 (2.17 EOL'd Aug 2025). Tests: 14 → 23 (9 added). Audit: memory/clawdforge-audits/java-0d3ee26.md
This commit is contained in:
parent
7745c5eb5c
commit
9866e97977
9 changed files with 443 additions and 105 deletions
|
|
@ -27,52 +27,55 @@ public class Basic {
|
|||
System.exit(2);
|
||||
}
|
||||
|
||||
ForgeClient client = ForgeClient.builder()
|
||||
try (ForgeClient client = ForgeClient.builder()
|
||||
.baseUrl(baseUrl)
|
||||
.token(token)
|
||||
.build();
|
||||
.build()) {
|
||||
|
||||
// 1. health
|
||||
HealthStatus h = client.healthz();
|
||||
System.out.printf("ok=%s claude_present=%s version=%s%n",
|
||||
h.ok(), h.claudePresent(), h.claudeVersion());
|
||||
// 1. health
|
||||
HealthStatus h = client.healthz();
|
||||
System.out.printf("ok=%s claude_present=%s version=%s%n",
|
||||
h.ok(), h.claudePresent(), h.claudeVersion());
|
||||
|
||||
// 2. run a prompt asking for JSON
|
||||
RunResult res = client.run(RunRequest.builder()
|
||||
.prompt("Reply with JSON: {\"hello\": \"world\"}")
|
||||
.model("sonnet")
|
||||
.timeoutSecs(60)
|
||||
.build());
|
||||
|
||||
System.out.printf("duration=%dms stop_reason=%s%n", res.durationMs(), res.stopReason());
|
||||
JsonNode r = res.result();
|
||||
if (r.isObject() && r.has("hello")) {
|
||||
System.out.println("hello -> " + r.get("hello").asText());
|
||||
} else if (r.isTextual()) {
|
||||
System.out.println("text -> " + r.asText());
|
||||
} else {
|
||||
System.out.println("raw -> " + r);
|
||||
}
|
||||
|
||||
// 3. (optional) upload + reference a file
|
||||
String filePath = System.getenv("CLAWDFORGE_FILE");
|
||||
if (filePath != null && !filePath.isBlank()) {
|
||||
FileToken ft = client.uploadFile(Path.of(filePath), 3600);
|
||||
System.out.println("uploaded " + ft.fileToken() + " (" + ft.size() + " bytes)");
|
||||
|
||||
RunResult res2 = client.run(RunRequest.builder()
|
||||
.prompt("Summarize the attached file in one sentence.")
|
||||
.files(List.of(ft.fileToken()))
|
||||
// 2. run a prompt asking for JSON
|
||||
RunResult res = client.run(RunRequest.builder()
|
||||
.prompt("Reply with JSON: {\"hello\": \"world\"}")
|
||||
.model("sonnet")
|
||||
.timeoutSecs(60)
|
||||
.build());
|
||||
System.out.println("summary -> " + res2.result());
|
||||
}
|
||||
|
||||
// 4. (admin) list tokens — only works with the admin bootstrap token
|
||||
if (Boolean.parseBoolean(System.getenv().getOrDefault("CLAWDFORGE_ADMIN", "false"))) {
|
||||
List<AppToken> tokens = client.listTokens();
|
||||
for (AppToken t : tokens) {
|
||||
System.out.printf("token name=%s created_at=%d ip_cidrs=%s%n",
|
||||
t.name(), t.createdAt(), t.ipCidrs());
|
||||
System.out.printf("duration=%dms stop_reason=%s%n", res.durationMs(), res.stopReason());
|
||||
JsonNode r = res.result();
|
||||
if (r.isObject() && r.has("hello")) {
|
||||
System.out.println("hello -> " + r.get("hello").asText());
|
||||
} else if (r.isTextual()) {
|
||||
System.out.println("text -> " + r.asText());
|
||||
} else if (r.isMissingNode()) {
|
||||
System.out.println("(no result field in response)");
|
||||
} else {
|
||||
System.out.println("raw -> " + r);
|
||||
}
|
||||
|
||||
// 3. (optional) upload + reference a file
|
||||
String filePath = System.getenv("CLAWDFORGE_FILE");
|
||||
if (filePath != null && !filePath.isBlank()) {
|
||||
FileToken ft = client.uploadFile(Path.of(filePath), 3600);
|
||||
System.out.println("uploaded " + ft.fileToken() + " (" + ft.size() + " bytes)");
|
||||
|
||||
RunResult res2 = client.run(RunRequest.builder()
|
||||
.prompt("Summarize the attached file in one sentence.")
|
||||
.files(List.of(ft.fileToken()))
|
||||
.build());
|
||||
System.out.println("summary -> " + res2.result());
|
||||
}
|
||||
|
||||
// 4. (admin) list tokens — only works with the admin bootstrap token
|
||||
if (Boolean.parseBoolean(System.getenv().getOrDefault("CLAWDFORGE_ADMIN", "false"))) {
|
||||
List<AppToken> tokens = client.listTokens();
|
||||
for (AppToken t : tokens) {
|
||||
// AppToken.toString() redacts the plaintext bearer.
|
||||
System.out.println("token " + t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue