Files
specs/openspec/changes/archive/2026-08-24-address-dependency-advisories-and-repo-hygiene/design.md
T

189 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Context
См. motivation в `proposal.md`. На старте change frontend напрямую использует
`react-router@7.9.6`; backend получает `deepmerge-ts@7.1.5` транзитивно через
`prisma@7.9.1 -> @prisma/config`. Frontend одновременно хранит одинаковые
`.env` и `.env.example`, а backend хранит пустой tracked `dev.db`; текущий
backend ignore rule покрывает только `prisma/*.db`.
Каждый application directory — отдельный Git repository с собственным
lockfile. Prisma 7.9.1 также является причиной deferred WL-P2-05, поэтому
dependency change не может считать e2e gate восстановленным без отдельного
подтверждения его review trigger.
### Baseline 2026-08-24
| Область | Наблюдение |
| --- | --- |
| Frontend dependency | direct `react-router@7.9.6` |
| Backend dependency path | `prisma@7.9.1 -> @prisma/config@7.9.1 -> deepmerge-ts@7.1.5`; `@prisma/client` и adapter также `7.9.1` |
| Frontend environment | `.env` и `.env.example` tracked с одинаковым content; local `.env` не игнорируется |
| Backend SQLite | пустой root `dev.db` tracked; ignore покрывает только `prisma/*.db` |
| Local URLs | development template направляет DB в `prisma/dev.db`; e2e config направляет её в `/tmp/wealth-ledger-e2e.db` |
### Advisory assessment 2026-08-24
`npm audit --omit=dev` reproduces one high frontend finding and three high
backend findings. The direct frontend path is `react-router@7.9.6`.
GHSA-2w69-qvjg-hvjx affects React Router `>=7.0.0, <=7.11.0` and is patched
in `7.12.0`; npm identifies `7.18.2` as the current non-major version that
also resolves the broader audit result. WealthLedger uses only
declarative `<BrowserRouter>`, static internal `Link` targets and local
`navigate` calls. It has no loaders, actions, SSR, RSC, single-fetch or
prerendering surface. This removes the known preconditions for
GHSA-2w69-qvjg-hvjx, but does not justify retaining an old direct package:
the supported fixed version is compatible with Node 22 and React 19 and will
be adopted.
The backend findings resolve through
`prisma@7.9.1 -> @prisma/config@7.9.1 -> deepmerge-ts@7.1.5`.
GHSA-ggr8-5vv4-36mx affects deepmerge-ts `<8.0.0` and is patched in `8.0.0`.
Source code does not import `@prisma/config` or `deepmerge-ts`; the former is
used by the local Prisma CLI configuration, while runtime uses the generated
Prisma client and SQLite adapter. The vulnerability requires two
attacker-controlled recursive object graphs passed into deepmerge APIs, a path
absent from the delivered HTTP service. npm offers only a Prisma 6.12.0 major
downgrade and the published latest Prisma remains 7.9.1, so this change
accepts the tooling-only residual risk rather than forcing an unsupported
override.
Review trigger: a vendor-supported Prisma release for the 7.x integration
that resolves `deepmerge-ts@>=8`, or new evidence that public runtime input
reaches a deepmerge API.
### Implemented dependency outcome
`react-router` is upgraded from `7.9.6` to `7.18.2`, its current
non-major npm audit fix. The existing AppRoutes regression suite validates
the delivered declarative routing contract after the update, and the frontend
production dependency audit reports zero findings. The Prisma/deepmerge-ts
finding is accepted as tooling-only residual risk under the review trigger
above; no manifest, lockfile or runtime behavior is changed for it.
### Implemented hygiene outcome
Each application repository exposes a non-mutating `hygiene:check` command.
The frontend check permits only tracked `.env.example`, requires local
`.env*` files to be ignored, and is part of `npm run verify`. The backend
check rejects tracked SQLite runtime files and verifies root/Prisma DB and
sidecar patterns without treating text fixtures as databases. The redundant
tracked frontend `.env` and empty backend root `dev.db` are removed; existing
documented URLs still create development data under ignored `prisma/` and e2e
data under `/tmp`.
### Application-gate evidence
Under Node `v22.21.0` and npm `10.9.4`, frontend `npm run verify` passes with
17 test files / 55 tests after the Router update. Backend `npm ci`,
`hygiene:check`, non-mutating lint, application/test typechecks and
`npm test -- --runInBand` pass with 19 suites / 59 tests. `npm run test:e2e`
still stops during `prisma migrate deploy` with the pre-existing empty
`Schema engine error` before Jest starts. This is recorded as the WL-P2-05
deferred outcome, not as a passing e2e result or evidence of a Prisma fix.
`npm audit --omit=dev` after the clean install reports zero frontend
production findings. Backend still reports three high findings in the exact
Prisma 7.9.1 path recorded above, with only a Prisma 6.12.0 major downgrade as
an automatic fix. OpenSpec authority, strict change validation, all synced
spec validation (16/16) and `git diff --check` in every affected repository
pass.
The remediation registry is updated with terminal evidence only after archive.
It is deliberately not a checkbox in `tasks.md`: the archive lifecycle
requires every task to be complete before archive, so post-archive bookkeeping
cannot also be an unfinished implementation task.
## Goals / Non-Goals
**Goals:**
- Получить evidence-based решение по каждому high advisory, а не скрыть его
массовым обновлением.
- Применить только минимальные поддерживаемые dependency updates и доказать
их существующими non-mutating gates.
- Сделать local environment и SQLite artifacts непригодными для случайного
commit, не ломая documented bootstrap.
**Non-Goals:**
- Автоматически обновлять все устаревшие packages, добавлять package manager
или вводить blanket `overrides` без vendor-supported compatibility path.
- Менять HTTP/OpenAPI contract, Prisma schema/migrations, production data,
T-Bank integration или обходить deferred Prisma e2e gate.
- Создавать CI/CD security scanner, secret-management service или хранить
реальные секреты в versioned files.
## Decisions
### Каждый advisory проходит assessment до изменения manifest
Для WL-P2-12 сначала фиксируются исходные advisory metadata, installed path,
affected version range, runtime/build reachability и минимальный supported
fixed candidate. Затем выбирается один из outcomes: coordinated upgrade,
accepted risk или deferred risk с конкретным trigger. `npm audit fix --force`
не используется: он смешивает unrelated upgrades и не создаёт evidence
применимости.
Альтернатива — обновить все findings одной командой audit fix. Она отвергнута,
потому что Prisma является источником отдельного e2e failure и semver-compatible
разрешение не доказывает поведение Nest/Prisma integration.
### Transitive Prisma finding обновляется только согласованной цепочкой
Если assessment требует remediation deepmerge-ts, change обновляет Prisma
packages только в vendor-supported, lockstep-compatible наборе и проверяет
фактический resolved graph после чистой установки. Точечный npm override
транзитивного package допустим только если Prisma официально поддерживает этот
диапазон; иначе finding остаётся документированным accepted/deferred risk.
Альтернатива — принудительно override-ить `deepmerge-ts`. Она отвергнута по
умолчанию, поскольку обходит ownership dependency chain и может скрыть
несовместимость Prisma tooling.
### Templates versioned, рабочие значения local
Frontend сохраняет единственный versioned template `.env.example`; `.env` и
варианты `.env.*` игнорируются, кроме явно разрешённого example. При удалении
tracked `.env` его содержимое сначала сверяется с example, поэтому documented
`cp .env.example .env` остаётся воспроизводимым. Backend `dev.db` удаляется
только как пустой runtime artifact; ignore policy покрывает root и Prisma
SQLite locations, не затрагивая будущие намеренно versioned fixtures.
Альтернатива — оставить безопасный сегодня `.env` tracked. Она отвергнута:
Git policy должна предотвращать будущий commit с секретом, а не оценивать
только текущее значение.
### Evidence собирается существующими gates
После каждой manifest/lockfile change выполняются `npm ci`, dependency graph и
relevant audit command в затронутом repository, затем существующие frontend
`npm run verify` и backend non-mutating lint/typecheck/unit-test gates. Любая
Prisma-related проверка отдельно сообщает исход `migrate deploy`; её failure
не маскируется и не переводит WL-P2-05/P2-13 в done.
## Risks / Trade-offs
- [Минимальный fixed release меняет router behavior или peer resolution] →
выполнить targeted route/auth regression tests и полный frontend verify до
принятия lockfile.
- [Prisma update меняет schema-engine behavior или incompatibly разрешает
adapter/client packages] → обновлять vendor-supported packages вместе,
запускать generated/type/unit gates и сохранить фактический e2e outcome.
- [Ignore rule слишком широка и скрывает fixture] → покрыть только runtime
DB patterns, проверить tracked files и явно оставить fixtures вне policy.
- [Удаление tracked `.env` оставляет разработчика без config] → template и
documented copy command проверяются до удаления tracked файла.
## Migration Plan
1. Зафиксировать baseline assessments и выбрать outcome для каждого advisory.
2. Выполнить согласованные dependency/lockfile updates или записать accepted/
deferred decision с review trigger.
3. Перевести frontend configuration на `.env.example` + ignore policy;
удалить tracked runtime inputs из index, сохранив локально воспроизводимый
file через template.
4. Удалить пустой backend runtime DB artifact, расширить ignore policy и
проверить temporary DB location без касания production schema.
5. Выполнить required gates и, только после archive, добавить terminal
evidence в remediation registry. Rollback — независимый Git revert в
затронутом repository; DB migration или user-data recovery не требуются.