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
7 changes: 7 additions & 0 deletions .jules/palette.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 2024-05-18 - Disabled and Loading States for Async Actions
**Learning:** Adding explicit loading and disabled states to asynchronous action buttons (like "Refresh") provides immediate feedback, reducing user confusion and preventing double-submissions.
**Action:** Always ensure that buttons triggering network requests visually indicate the loading state and are disabled until the request completes.

## 2026-07-07 - 비동기 μž‘μ—… λ²„νŠΌμ— λͺ…μ‹œμ μΈ λ‘œλ”© μƒνƒœ ν•„μš”
**ν•™μŠ΅λ‚΄μš©:** `demo.js`의 데λͺ¨ UI λ‚΄ 비동기 μž‘μ—… λ²„νŠΌλ“€μ΄ 처리 κ³Όμ • 쀑 μ‹œκ°μ  ν”Όλ“œλ°±μ„ μ œκ³΅ν•˜μ§€ λͺ»ν•˜λŠ” μ ‘κ·Όμ„±/UX 문제λ₯Ό λ°œκ²¬ν–ˆμŠ΅λ‹ˆλ‹€. μ΄λŸ¬ν•œ ν”Όλ“œλ°± λΆ€μž¬λŠ” μ‚¬μš©μžμ˜ ν˜Όλž€μ„ μ΄ˆλž˜ν•˜κ³  쀑볡 μ œμΆœμ„ ν—ˆμš©ν•˜μ—¬ μ „λ°˜μ μΈ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ κ²½ν—˜μ„ μ €ν•˜μ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€.
**μ μš©λ°©λ²•:** λͺ¨λ“  비동기 μž‘μ—… λ²„νŠΌμ΄ μ‹€ν–‰λ˜λŠ” λ™μ•ˆ λ²„νŠΌμ˜ ν…μŠ€νŠΈ(예: "Submitting...", "Retrying...", "Refreshing...")λ₯Ό λͺ…μ‹œμ μœΌλ‘œ λ³€κ²½ν•˜κ³  `disabled` 속성을 true둜 μ„€μ •ν•˜μ—¬ λ‘œλ”© μƒνƒœμž„μ„ λͺ…ν™•νžˆ λ‚˜νƒ€λ‚΄λ„λ‘ ν•©λ‹ˆλ‹€. 였λ₯˜ λ°œμƒ μ‹œ μ‚¬μš©μžκ°€ μž‘λ™ 뢈λŠ₯ μƒνƒœμ— λΉ μ§€μ§€ μ•Šλ„λ‘ `finally` λΈ”λ‘μ—μ„œ κΈ°μ‘΄ μƒνƒœλ‘œ λ³΅μ›ν•©λ‹ˆλ‹€.
## 2026-07-07 - Test Coverage Issue with UX Adjustments
**Learning:** Found that frontend JS changes could be made without updating tests and 100% test coverage instruction was explicitly provided but the project only has backend tests in JUnit/Mockito, meaning javascript changes do not show up in the jacoco test coverage metric. The JS changes don't have unit tests to augment.
**Action:** When working on UX issues inside vanilla Javascript files, verify frontend logic manually via e2e checks and UI verifications, and note that there are no javascript unit tests to fulfill the 100% coverage requirement.
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 - Bouncy Castle CVE-2025-14813 Mitigation
**Vulnerability:** Bouncy Castle `bcprov-jdk18on` version 1.81 contains a critical vulnerability (CVE-2025-14813) involving GOSTCTR implementation processing failures, leading to potential security issues. This was flagged by Trivy security scanner during the CI pipeline.
**Learning:** Security dependencies, particularly cryptography-related ones, require vigilant monitoring and prompt updates when critical vulnerabilities are disclosed.
**Prevention:** Ensured the exact dependency version mapping in `pom.xml` handles the vulnerable versions by explicitly updating to `1.81.1` via the `<bouncycastle.version>` property, which securely resolves the issue without breaking downstream `pdfbox` requirements.
4 changes: 2 additions & 2 deletions docs/security/2026-07-02-license-policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
],
"reviewRequiredComponents": [
{
"purl": "pkg:maven/ch.qos.logback/logback-classic@1.5.18?type=jar",
"purl": "pkg:maven/ch.qos.logback/logback-classic@1.5.32?type=jar",
"reason": "Dual EPL/LGPL metadata; legal must confirm selected license route."
},
{
"purl": "pkg:maven/ch.qos.logback/logback-core@1.5.18?type=jar",
"purl": "pkg:maven/ch.qos.logback/logback-core@1.5.32?type=jar",
"reason": "Dual EPL/LGPL metadata; legal must confirm selected license route."
},
{
Expand Down
30 changes: 29 additions & 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 All @@ -24,6 +24,13 @@
<argLine></argLine>
<pdfbox.version>3.0.3</pdfbox.version>
<pdfjs.dist.version>4.10.38</pdfjs.dist.version>
<bouncycastle.version>1.81.1</bouncycastle.version>
<spring-boot.version>3.5.14</spring-boot.version>
<commons-io.version>2.18.0</commons-io.version>
<netty.version>4.1.135.Final</netty.version>
<jackson-databind.version>2.18.8</jackson-databind.version>
<commons-lang3.version>3.18.0</commons-lang3.version>
<spring-framework.version>6.2.18</spring-framework.version>
</properties>

<dependencies>
Expand All @@ -32,6 +39,27 @@
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>

Check warning

Code scanning / Trivy

bouncycastle: BC-JAVA: LDAP injection vulnerability in LDAPStoreHelper.java Medium

Package: org.bouncycastle:bcprov-jdk18on
Installed Version: 1.81.1
Vulnerability CVE-2026-0636
Severity: MEDIUM
Fixed Version: 1.84
Link: CVE-2026-0636
Comment on lines +42 to +46
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcutil-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>

Check warning

Code scanning / Trivy

bouncycastle: BC-JAVA: PKIX draft CompositeVerifier accepts empty signature sequence as valid Medium

Package: org.bouncycastle:bcpkix-jdk18on
Installed Version: 1.81.1
Vulnerability CVE-2026-5588
Severity: MEDIUM
Fixed Version: 1.84
Link: CVE-2026-5588
Comment on lines +52 to +56
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcjmail-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
Expand Down
18 changes: 16 additions & 2 deletions src/main/resources/static/assets/viewer/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,9 @@ async function retryActiveJob() {

const jobId = activeJobDetail.jobId;
setStatus("Requesting operator retry...");
const originalRetryText = el.retryJobBtn.textContent;
el.retryJobBtn.disabled = true;
el.retryJobBtn.textContent = "Retrying...";
try {
const res = await fetch(`/api/v1/convert/jobs/${encodeURIComponent(jobId)}/retry`, {
method: "POST",
Expand Down Expand Up @@ -308,6 +310,7 @@ async function retryActiveJob() {
setError("Network error while requesting retry. Retry when the service is reachable.");
} finally {
el.retryJobBtn.disabled = false;
el.retryJobBtn.textContent = originalRetryText;
}
}

Expand Down Expand Up @@ -435,7 +438,9 @@ async function submitDocument(event) {
return;
}

const originalSubmitText = el.submitBtn.textContent;
el.submitBtn.disabled = true;
el.submitBtn.textContent = "Submitting...";
setStatus("Submitting document...");

try {
Expand Down Expand Up @@ -479,6 +484,7 @@ async function submitDocument(event) {
setError("Network error while submitting. Retry when the service is reachable.");
} finally {
el.submitBtn.disabled = false;
el.submitBtn.textContent = originalSubmitText;
}
}

Expand Down Expand Up @@ -515,8 +521,16 @@ function init() {
el.retryJobBtn.addEventListener("click", () => {
void retryActiveJob();
});
el.refreshEvidenceBtn.addEventListener("click", () => {
void refreshKpiEvidence();
el.refreshEvidenceBtn.addEventListener("click", async () => {
const originalText = el.refreshEvidenceBtn.textContent;
el.refreshEvidenceBtn.disabled = true;
el.refreshEvidenceBtn.textContent = "Refreshing...";
try {
await refreshKpiEvidence();
} finally {
el.refreshEvidenceBtn.disabled = false;
el.refreshEvidenceBtn.textContent = originalText;
}
});
}

Expand Down
Loading