Skip to content
Closed
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
5 changes: 5 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,8 @@
**Vulnerability:** The document hashing routine in `DefaultDocumentConversionService` processed file streams without enforcing any maximum size limit on the bytes read. An attacker could exploit this by uploading a maliciously large stream (or exploiting a compression bomb if unzipping), exhausting system memory, CPU, or disk space (DoS).
**Learning:** Checking the declared file size (e.g., `file.getSize()`) in initial validation is not always sufficient if the input stream itself can be spoofed or dynamically expanded during reading. The actual bytes read must be verified against bounds continuously.
**Prevention:** Always enforce a strict, configurable size limit (e.g., `ConversionProperties.maxUploadSizeBytes`) within the `while` loop that reads from untrusted input streams. Track `totalRead` and throw an exception immediately if the limit is exceeded.

## 2026-08-19 - 런타임 시크릿 설정 방식 변경
**Vulnerability:** 환경 변수를 통해 런타임 시크릿(artifact token secret 및 tenant claims HMAC secret)을 직접 주입하고 있었으며, 이는 컨테이너나 프로세스 환경에 민감한 정보가 노출될 위험이 존재했습니다.
**Learning:** `AGENTS.md`의 보안 지침에 따라 환경 변수는 런타임 애플리케이션의 설정 값이 아닌 KV 저장소(configtree 등)를 부트스트랩하기 위한 경로로만 사용되어야 합니다. 또한 Spring Boot 설정에서 `@Value` 어노테이션의 기본값을 완전히 제거하여 설정 트리의 값이 없을 경우 빈 문자열로 덮어씌워지지 않도록 해야 합니다.
**Prevention:** 향후 런타임 시크릿 및 권한 증명과 관련된 새로운 변수가 추가될 때, 환경 변수 주입 방식이 아닌 반드시 KV 기반의 조회 방식(configtree 마운트 등)과 `@Value("${property.name}")` 형식의 기본값 없는 엄격한 주입을 사용해야 합니다.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,6 @@
- 루트 `LICENSE`와 Maven license metadata를 추가해 Scorecard License alert가 표준 Apache-2.0 파일을 확인할 수 있게 했습니다.
- logback-core 신규 권고(GHSA-jhq6-gfmj-v8fx) 대응을 위해 Logback 관리 버전을 `1.5.35`로 고정했습니다.
- 저장소 보안 정책, Maven/GitHub Actions Dependabot 설정, 기본 CodeQL/중앙 SAST 운영 지침, 다운로드 파일명 정규화 Jazzer fuzz target을 추가해 Scorecard 보안 거버넌스 신호를 보강했습니다.

### 보안 (Security)
- 런타임 환경 변수 대신 Spring Boot configtree를 통해 인증 시크릿(artifact token secret 및 tenant claims HMAC secret)을 로드하도록 마이그레이션했습니다.
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public class ArtifactLinkService {
public ArtifactLinkService(
ArtifactStore artifactStore,
ArtifactLinkLedger artifactLinkLedger,
@Value("${clearfolio.artifact-token.secret:}") String configuredSecret) {
@Value("${clearfolio.artifact-token.secret}") String configuredSecret) {
this(artifactStore, artifactLinkLedger, configuredSecret, Clock.systemUTC(), new SecureRandom());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public TenantAccessService() {
*/
@Autowired
public TenantAccessService(
@Value("${clearfolio.tenant-claims.hmac-secret:}") String claimsHmacSecret,
@Value("${clearfolio.tenant-claims.hmac-secret}") String claimsHmacSecret,
@Value("${clearfolio.tenant-claims.max-skew-seconds:300}") long maxSkewSeconds) {
this(claimsHmacSecret, maxSkewSeconds, Clock.systemUTC());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class ProductionAuthReadinessConfig {
* @param tenantClaimsSecret shared gateway signing secret
*/
public ProductionAuthReadinessConfig(
@Value("${clearfolio.tenant-claims.hmac-secret:}") String tenantClaimsSecret) {
@Value("${clearfolio.tenant-claims.hmac-secret}") String tenantClaimsSecret) {
if (!StringUtils.hasText(tenantClaimsSecret)) {
throw new IllegalStateException(
"production profile requires clearfolio.tenant-claims.hmac-secret"
Expand Down
2 changes: 0 additions & 2 deletions src/main/resources/application-buyer-demo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ viewer:

clearfolio:
artifact-token:
secret: ${CLEARFOLIO_ARTIFACT_TOKEN_SECRET:}
tenant-claims:
hmac-secret: ${CLEARFOLIO_TENANT_CLAIMS_HMAC_SECRET:}
max-skew-seconds: ${CLEARFOLIO_TENANT_CLAIMS_MAX_SKEW_SECONDS:300}
artifact-link-ledger:
path: ${CLEARFOLIO_ARTIFACT_LINK_LEDGER_PATH:}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@
import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;

class ProductionAuthReadinessConfigTest {

@Test
void productionProfileFailsWithoutSignedTenantClaimsSecret() {
productionRunner().run(context -> assertThat(context.getStartupFailure())
.hasRootCauseMessage("production profile requires clearfolio.tenant-claims.hmac-secret"));
productionRunner()
.withPropertyValues("clearfolio.tenant-claims.hmac-secret=")
.run(context -> {
assertThat(context.getStartupFailure()).isNotNull();
assertThat(context.getStartupFailure()).hasRootCauseMessage("production profile requires clearfolio.tenant-claims.hmac-secret");
});
}

@Test
Expand Down
Loading