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
+21 -18
View File
@@ -3,7 +3,7 @@
.gitignore
# Dependencies
node_modules
**/node_modules
.pnpm-store
# Build output
@@ -11,26 +11,29 @@ dist
coverage
# Local environment files (a committed .env.example lands in E00-S04)
.env
.env.*
**/.env
**/.env.*
# Secrets & credentials (E00-S02-T08) — never part of the build context, so a
# secret-bearing file cannot be embedded in the image even if a developer has
# one locally. Keep this list in sync with tests/secrets-not-embedded.test.mjs.
.npmrc
.netrc
.credentials
.aws
.ssh
secrets
secrets/
*.pem
*.key
*.p12
*.pfx
*.jks
id_rsa
id_ed25519
# one locally. Every pattern is `**/`-prefixed because Docker's matcher
# (moby/patternmatcher) anchors a slash-less pattern to the context ROOT — a
# bare `.npmrc`/`*.key`/`secrets` would exclude nothing under `apps/server/…`.
# `**/` matches the file at the root AND at any nested depth. Keep this list
# in sync with tests/secrets-not-embedded.test.mjs.
**/.npmrc
**/.netrc
**/.credentials
**/.aws
**/.ssh
**/secrets
**/*.pem
**/*.key
**/*.p12
**/*.pfx
**/*.jks
**/id_rsa
**/id_ed25519
# Logs
*.log