fix: make .dockerignore exclusions apply at any depth (E00-S02-T08)
CI / Frozen lockfile install (pull_request) Successful in 52s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 36s

Resolve the security review of #389 (findings 1-4):

- .dockerignore: every env/credential pattern is now **/-prefixed
  (**/.env, **/.env.*, **/node_modules, **/.npmrc, ..., **/secrets,
  **/*.pem, **/*.key, ...) and the redundant 'secrets/' line is dropped.
  Docker's matcher (moby/patternmatcher) anchors slash-less patterns to
  the context root, so the bare forms excluded nothing under apps/server/;
  **/ matches the root AND any nested depth. (finding 1, 3)
- tests/secrets-not-embedded.test.mjs: the dockerignore matcher is now a
  faithful port of moby/patternmatcher (filepath.Clean + anchored full-path
  match + parent-directory propagation), not gitignore basename semantics;
  asserts nested example paths (apps/server/.npmrc, config/server.key,
  apps/server/secrets/...) are excluded; requires no redundant equivalent
  patterns verbatim; adds mutation probes for bare-pattern and
  duplicate-pattern regressions. (finding 2, 3)
- apps/server/Dockerfile + compose.yaml: guarantee restated precisely
  (credential files excluded at the context root AND at any depth).
- .gitea/workflows/ci.yml: new job runs
  'node --test tests/secrets-not-embedded.test.mjs' on every PR; the
  docker-gated layer-scan probe runs where a daemon exists, skips cleanly
  otherwise. (finding 4)
- tests/compose-config.test.mjs: .dockerignore presence list updated to the
  **/-prefixed forms (node_modules, .env).

Tested: secrets suite 16 tests -> 15 pass / 1 docker-gated skip / 0 fail;
full suite 101 pass / 12 fail / 8 skip, failures identical to clean main
(env-dependent pnpm/Node-24 suites); matcher port verified against the
moby/patternmatcher evidence table.
This commit is contained in:
implementer
2026-08-29 01:49:07 +00:00
parent b3ad55efe8
commit f00c13d57a
6 changed files with 394 additions and 116 deletions
+4 -1
View File
@@ -445,7 +445,10 @@ test('the build context excludes local artifacts and environment files', () => {
.split(/\r?\n/)
.map((line) => line.trim())
.filter((line) => line && !line.startsWith('#'));
for (const required of ['node_modules', 'dist', '.env', '.git']) {
// T08 hardened these to their `**/`-prefixed forms so the exclusions also
// apply at any nested depth (Docker's matcher anchors slash-less patterns to
// the context root).
for (const required of ['**/node_modules', 'dist', '**/.env', '.git']) {
assert.ok(
patterns.includes(required),
`.dockerignore must exclude "${required}" (got: ${patterns.join(', ')})`,