Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2026-07-08 - Optimize Byte-to-Hex Conversion
**Learning:** Using `String.format("%02x", b)` in a loop for converting byte arrays to hex strings introduces unnecessary overhead and intermediate string allocations in Java.
**Action:** Use a reusable `java.util.HexFormat` instance (`private static final java.util.HexFormat HEX_FORMAT = java.util.HexFormat.of();`) instead of `String.format` loops or repeated `HexFormat.of()` calls to optimize performance and reduce intermediate allocations.
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
**Vulnerability:** Untrusted paths from API responses were directly assigned to `a.href` and used in `iframe` generation, which allows execution of malicious URIs like `javascript:` or `data:`.
**Learning:** Even when avoiding `innerHTML`, directly setting URL-like strings to DOM attributes without protocol validation introduces XSS vectors. The payload can be executed when the link is clicked or the iframe is loaded.
**Prevention:** Implement an `isSafeUrl` verification function to ensure the protocol is strictly `http:` or `https:` (using `new URL()`) before assigning untrusted inputs to DOM attributes like `href` or `src`.
## 2026-07-08 - Fix Strix Penetration Test Vulnerability
**Vulnerability:** Truncated Hash in Token Fingerprint. The `tokenFingerprint` method truncated SHA-256 hashes to 8 bytes, reducing security and increasing collision risks.
**Learning:** Truncating cryptographic hashes can compromise the strength of the hashing algorithm and increase the risk of collision attacks.
**Prevention:** Use the full hash output for validation or cryptographic purposes to maintain security strength.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.0</version>
<version>3.5.14</version>
<relativePath/>
</parent>

Expand Down
7 changes: 7 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[tool.pytest.ini_options]
minversion = "6.0"
addopts = "-ra -q"
testpaths = [
"tests",
"integration",
]
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.time.Instant;
import java.util.Arrays;
import java.util.Base64;
import java.util.HexFormat;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
Expand Down Expand Up @@ -57,6 +58,7 @@ public class ArtifactLinkService {
private static final int TOKEN_FIELD_COUNT = 10;
private static final Base64.Encoder URL_ENCODER = Base64.getUrlEncoder().withoutPadding();
private static final Base64.Decoder URL_DECODER = Base64.getUrlDecoder();
private static final HexFormat HEX_FORMAT = HexFormat.of();

private final ArtifactStore artifactStore;
private final ArtifactLinkLedger artifactLinkLedger;
Expand Down Expand Up @@ -423,11 +425,7 @@ private static String sha256Hex(byte[] bytes) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] raw = digest.digest(bytes);
StringBuilder hex = new StringBuilder(raw.length * 2);
for (byte b : raw) {
hex.append(String.format("%02x", b));
}
return hex.toString();
return HEX_FORMAT.formatHex(raw);
} catch (GeneralSecurityException ex) {
throw new IllegalStateException("SHA-256 digest unavailable", ex);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HexFormat;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
Expand All @@ -26,6 +27,8 @@
@Service
public class DefaultDocumentConversionService implements DocumentConversionService {

private static final HexFormat HEX_FORMAT = HexFormat.of();

private final ConversionJobRepository repository;
private final ConversionJobStateStore stateStore;
private final DocumentValidationService validationService;
Expand Down Expand Up @@ -160,12 +163,7 @@ private String contentHash(MultipartFile file) {
}

byte[] raw = digest.digest();
StringBuilder hex = new StringBuilder(raw.length * 2);
for (byte b : raw) {
hex.append(String.format("%02x", b));
}

return hex.toString();
return HEX_FORMAT.formatHex(raw);
} catch (NoSuchAlgorithmException ex) {
throw new IllegalStateException("SHA-256 digest unavailable", ex);
} catch (IOException ex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ private String tokenFingerprint(String approvalToken) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hashed = digest.digest(approvalToken.getBytes(StandardCharsets.UTF_8));
return HexFormat.of().formatHex(hashed, 0, FINGERPRINT_TRUNCATE_BYTES);
return HexFormat.of().formatHex(hashed);
} catch (NoSuchAlgorithmException ex) {
throw new IllegalStateException("SHA-256 digest unavailable", ex);
}
Expand Down
10 changes: 10 additions & 0 deletions src/main/resources/static/assets/viewer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "clearfolio-viewer-assets",
"version": "1.0.0",
"description": "Frontend assets for Clearfolio Viewer",
"private": true,
"scripts": {
"test": "echo \"No test specified\"",
"lint": "echo \"No linting specified\""
}
}
Loading