From 5d5e38b9583c2b669ccfc9db016db8970e94a224 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 12 Aug 2026 06:49:43 +0900 Subject: [PATCH 1/4] test(security): reproduce local env tracking gap on live develop --- .../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 40e87740ce0495c6d13cbef9769d9be3140dd9db Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 12 Aug 2026 06:52:53 +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..e6a0584e 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,11 @@ package-lock.json *.bak .~* +# Local environment credentials +.env +.env.* +!.env.example + # OpenCode (local CLI artifacts) registered_agents.json task_agent_mapping.json From 308623a56e20cbef28a7b63681421d93d4f31f69 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 12 Aug 2026 06:55:13 +0900 Subject: [PATCH 3/4] test(security): reproduce Docker env build-context gap --- .../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 960f3bafe915e39334c6eaf4070e05fd22bd292d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 12 Aug 2026 07:09:08 +0900 Subject: [PATCH 4/4] fix(security): exclude local env files from Docker context --- .dockerignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.dockerignore b/.dockerignore index bcd2cab5..c1d61ae3 100644 --- a/.dockerignore +++ b/.dockerignore @@ -8,3 +8,8 @@ **/*.swp **/*.swo **/.DS_Store + +# Local environment credentials +.env +.env.* +!.env.example