From 541a7fd875b2bbe1d37cc708481bcb59fc6f5398 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 10 Aug 2026 14:37:41 +0900 Subject: [PATCH 1/4] test(security): fail when local env files are trackable --- .../RepositorySecretFilePolicyTest.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 etl-service/src/test/java/com/xtrmetl/etl/config/RepositorySecretFilePolicyTest.java diff --git a/etl-service/src/test/java/com/xtrmetl/etl/config/RepositorySecretFilePolicyTest.java b/etl-service/src/test/java/com/xtrmetl/etl/config/RepositorySecretFilePolicyTest.java new file mode 100644 index 00000000..85b9677d --- /dev/null +++ b/etl-service/src/test/java/com/xtrmetl/etl/config/RepositorySecretFilePolicyTest.java @@ -0,0 +1,43 @@ +package com.xtrmetl.etl.config; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * Guards the repository boundary that keeps developer-local environment files out of Git. + * + *

The local Compose guidance uses environment overrides for database and service credentials. + * Those files must remain untracked by default, while a non-secret {@code .env.example} template + * may remain reviewable in source control. + */ +class RepositorySecretFilePolicyTest { + + @Test + void localEnvironmentFilesAreIgnoredWhileExampleTemplateMayBeTracked() throws IOException { + Path repositoryRoot = findRepositoryRoot(Path.of("").toAbsolutePath().normalize()); + assertNotNull(repositoryRoot, "repository root containing .gitignore must be discoverable"); + + List ignoredPatterns = Files.readAllLines(repositoryRoot.resolve(".gitignore")); + + assertTrue(ignoredPatterns.contains(".env"), "root .gitignore must ignore .env"); + assertTrue(ignoredPatterns.contains(".env.*"), "root .gitignore must ignore environment-specific .env files"); + assertTrue(ignoredPatterns.contains("!.env.example"), "root .gitignore must permit a reviewed non-secret .env.example template"); + } + + private static Path findRepositoryRoot(Path start) { + Path candidate = start; + while (candidate != null) { + if (Files.isRegularFile(candidate.resolve(".gitignore"))) { + return candidate; + } + candidate = candidate.getParent(); + } + return null; + } +} From b6a673515da775505ab85bcc24c75956abaf9e80 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 10 Aug 2026 14:40:08 +0900 Subject: [PATCH 2/4] fix(security): ignore developer-local environment files --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 0418d2d7..c2124a9c 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,11 @@ hs_err_pid* node_modules/ package-lock.json +# Developer-local environment files +.env +.env.* +!.env.example + # Temporary files *.tmp *.bak From f73d4c460634a125ce0f087f81f56a56b4c01a46 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 10 Aug 2026 14:45:42 +0900 Subject: [PATCH 3/4] test(security): keep env secrets out of Docker context --- .../RepositorySecretFilePolicyTest.java | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/etl-service/src/test/java/com/xtrmetl/etl/config/RepositorySecretFilePolicyTest.java b/etl-service/src/test/java/com/xtrmetl/etl/config/RepositorySecretFilePolicyTest.java index 85b9677d..cb01d6d9 100644 --- a/etl-service/src/test/java/com/xtrmetl/etl/config/RepositorySecretFilePolicyTest.java +++ b/etl-service/src/test/java/com/xtrmetl/etl/config/RepositorySecretFilePolicyTest.java @@ -10,11 +10,13 @@ import org.junit.jupiter.api.Test; /** - * Guards the repository boundary that keeps developer-local environment files out of Git. + * Guards repository boundaries that keep developer-local environment files out of Git and Docker + * build contexts. * *

The local Compose guidance uses environment overrides for database and service credentials. - * Those files must remain untracked by default, while a non-secret {@code .env.example} template - * may remain reviewable in source control. + * Those files must remain untracked and excluded from Docker build transfer by default, while a + * reviewed non-secret {@code .env.example} template may remain available for source-controlled + * documentation.

*/ class RepositorySecretFilePolicyTest { @@ -23,11 +25,29 @@ void localEnvironmentFilesAreIgnoredWhileExampleTemplateMayBeTracked() throws IO Path repositoryRoot = findRepositoryRoot(Path.of("").toAbsolutePath().normalize()); assertNotNull(repositoryRoot, "repository root containing .gitignore must be discoverable"); - List ignoredPatterns = Files.readAllLines(repositoryRoot.resolve(".gitignore")); + assertEnvironmentFileRules( + Files.readAllLines(repositoryRoot.resolve(".gitignore")), + "root .gitignore" + ); + } + + @Test + void localEnvironmentFilesAreExcludedFromDockerBuildContext() throws IOException { + Path repositoryRoot = findRepositoryRoot(Path.of("").toAbsolutePath().normalize()); + assertNotNull(repositoryRoot, "repository root containing .gitignore must be discoverable"); + + Path dockerIgnore = repositoryRoot.resolve(".dockerignore"); + assertTrue(Files.isRegularFile(dockerIgnore), "root .dockerignore must exist"); + assertEnvironmentFileRules(Files.readAllLines(dockerIgnore), "root .dockerignore"); + } - assertTrue(ignoredPatterns.contains(".env"), "root .gitignore must ignore .env"); - assertTrue(ignoredPatterns.contains(".env.*"), "root .gitignore must ignore environment-specific .env files"); - assertTrue(ignoredPatterns.contains("!.env.example"), "root .gitignore must permit a reviewed non-secret .env.example template"); + private static void assertEnvironmentFileRules(List patterns, String boundary) { + assertTrue(patterns.contains(".env"), boundary + " must ignore .env"); + assertTrue(patterns.contains(".env.*"), boundary + " must ignore environment-specific .env files"); + assertTrue( + patterns.contains("!.env.example"), + boundary + " must permit a reviewed non-secret .env.example template" + ); } private static Path findRepositoryRoot(Path start) { From 3e37e937cc45d6bf40e9c6b39cf5c1de93aebe6c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 10 Aug 2026 14:46:57 +0900 Subject: [PATCH 4/4] fix(security): exclude local env files from Docker context --- .dockerignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.dockerignore b/.dockerignore index bcd2cab5..df965fd2 100644 --- a/.dockerignore +++ b/.dockerignore @@ -8,3 +8,6 @@ **/*.swp **/*.swo **/.DS_Store +.env +.env.* +!.env.example