Conversation
Move absolute filesystem paths out of `DatabaseError.message` and into
a new structured `details` bag (`{ path }`, `{ dir, reason }`). Pino's
redactor walks structured keys but not message content, so paths
concatenated into the message would otherwise leak into structured
audit / observability logs even with `DEFAULT_REDACT_PATHS` configured.
The factory signatures (`openFailed(path, cause)`,
`migrationDirectoryInvalid(dir, reason)`, ...) remain unchanged — only
the internal storage shape moves. Other factories also gain a
populated `details` bag (operation, version, name, sqlLength,
extensionName, dbVersion, codeMaxVersion) so callers have a single
non-message channel for structured fields.
Adds matching `details.path` / `details.dir` (plus `*.details.path` /
`*.details.dir`) globs to `DEFAULT_REDACT_PATHS` so the canonical
caller pattern `logger.error({ err }, "...")` redacts the path before
it lands in the JSON line.
Tests:
- `errors.test.ts`: assert `message` does NOT contain the absolute
path and that `details.path` / `details.dir` carry the original
value (backward-compat pivot for previous message-substring asserts).
- `pino-logger.test.ts`: end-to-end redact test feeding a synthetic
DatabaseError envelope through `logger.error` and asserting the
path never appears in stdout, while non-sensitive sibling fields
(`reason`) stay visible.
Refs: HANDOFF.md §6.6 / §6.21 row 4 — Security-Low observation noted
during Fase 2 to be addressed before v0.5 introduces structured audit
logging.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
h2devx
added a commit
that referenced
this pull request
May 3, 2026
) ## Summary Phase-17 close docs-only PR. Pattern matches PR #25 (Phase-12), #28 (Phase-13), #32 (Phase-14), #36 (Phase-15), #42 (Phase-16) — each cycle/phase ends with a docs-only PR synthesizing the closure. ## What Phase-17 delivered **v0.5 hardening defensivo cycle** — 4 incremental PRs squash-merged to \`develop\`: | # | PR | Warning | Fix | |---|---|---|---| | 1 | [#43](#43) | W-3.5-SEC-M2 | chmod 0o600 on recall.db | | 2 | [#44](#44) | W-3.5-SEC-M1 | atomic write+rename on .gitignore + writeConfig consolidated with CSPRNG suffix | | 3 | [#45](#45) | W-3.5-SEC-L1 (partial) | redact absolute paths from DatabaseError messages → \`details.path\` + 4 new pino redact globs | | 4 | [#46](#46) | W-3.1-SEC-M1 | configurable buffer cap on StdioJsonRpcServer (default 10 MiB) + env var override + transport closure on overflow | Zero security-auditor rejections (4 APPROVED WITH OBSERVATIONS). 1 CI round-trip in PR-2 over S7735 negated condition trivial fix. 36 new VALOR-asserting tests consolidated, 5+1 EXIT=0 green in each PR, SonarQube quality gate PASSED in each PR. ## Key finding tracked: W-3.5-SEC-L2 follow-up PR #45's security-auditor revealed that **W-3.5-SEC-L1 is NOT categorically closed** — only closed for DatabaseError. **9+ Error factories in workspace/secrets/curator modules** still interpolate \`rootPath\`/\`startPath\`/\`hookPath\` into \`message\`, and they flow to the wire JSON-RPC via \`error-mapper.ts\` Tier 3.5. Same leak pattern, also flowing to MCP clients. Affected files (tracked as W-3.5-SEC-L2 for next hardening cycle): - \`workspace/infrastructure/errors/workspace-infrastructure-error.ts\` (9 factories) - \`workspace/application/errors/workspace-application-error.ts\` (NoWorkspaceAtPathError) - \`secrets/infrastructure/errors/foreign-hook-exists-error.ts\` - \`curator/infrastructure/errors/curator-infrastructure-error.ts\` (scanFailed) Recommendation: apply same \`details: { path }\` pattern across all error factories before v0.5 GA. ## What this PR adds Pure HANDOFF.md changes (212 insertions / 8 deletions): - **§0**: 6 rows updated (Fecha, Fase actual, Lineas codigo, Tests, Issues abiertos, Proximo paso). - **§6.21**: roadmap row 4 (hardening defensivo) marked CLOSED in Phase-17. - **§6.22 NEW**: full Phase-17 cycle close section (decisions, sub-phases, detail per PR, consolidated observations table with 12 entries, 8 orchestrator decisions D-1701..D-1708, 5 durable lessons, repo state, next-action with 3 options for release). - **Footer** "Ultima actualizacion" updated to reflect Phase-17 closure. ## State of repo post-merge | Item | Value | |---|---| | HEAD develop | \`f23457e\` (4 commits ahead of main) | | HEAD main | \`29371f8\` (unchanged) | | Tag latest | \`v0.1.2\` (unchanged) | | npm dist-tags | \`{ latest: '0.1.2', beta: '0.1.2-beta.6' }\` (unchanged — Phase-17 publishes nothing) | | Tests | 2588 passing in 212 files (+28 vs Phase-16 baseline) | | Coverage | new 100% / overall 96.4% | | Hardening warnings closed | 4/4 | | Follow-ups tracked | 12 (1 medium W-3.5-SEC-L2 + 11 low/info) | | Issues open | 0 | | PRs open | 0 (after this merge) | ## Test plan - [x] No code changes — pure docs PR (HANDOFF.md only). - [x] Hooks pre-commit no-op (no \`code/src/\` changes → typecheck not triggered). - [x] CI required status check \`ci\`. - [x] SonarQube quality gate (no source files affected). ## Decision pending after merge **Cut \`release/0.1.3-beta.0\` now or later?** - **Option A** — cut now with 4 hardening fixes alone (Phase-9/12/14 cooling pattern: ship beta, dogfood real, fix what surfaces). - **Option B** — accumulate more changes (item #1 multi-key envelope, item #3 perf hardening, item #5 swap embedder) before next release. - **Option C** — defer release until a real bug surfaces in 0.1.2 stable ("first new bug + feature plus" pattern from §6.21). Recommendation: Option A aligns with project's historical cadence. Final call belongs to the human. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Third fix of the v0.5 hardening cycle (HANDOFF.md §6.21 roadmap row 4). Closes warning W-3.5-SEC-L1 for DatabaseError (HANDOFF §6.6).
DatabaseError.openFailed(path, cause)andDatabaseError.migrationDirectoryInvalid(dir, ...)previously interpolated absolute paths into themessagefield. Pino redacts keys (DEFAULT_REDACT_PATHS) but does NOT redact message content — paths leaked to structured logs / observability exporters, revealing user home dir + repo layout.This PR moves those paths from
messageto a new structured fielddetails: { path }/details: { dir, reason }. Public factory signatures are unchanged (backward-compat). Pino DEFAULT_REDACT_PATHS gains 4 new globs coveringdetails.path/details.dir(top-level + 1-segment-wrapped).What changed
Code
code/src/shared/infrastructure/errors/database-error.ts(+49/-10) —details: Readonly<Record<string, unknown>>field added; `openFailed` and `migrationDirectoryInvalid` move path/dir from message to details; 8 other factories gain consistent `details` shape with non-sensitive data (extension name, sql length, operation name, version numbers).code/src/shared/infrastructure/logger/pino-logger.ts(+12/-0) — DEFAULT_REDACT_PATHS gains: `details.path`, `details.dir`, `.details.path`, `.details.dir` (covers standalone + wrapped-in-err shapes).Tests (+12 tests, all VALOR)
Audit factories inventory (10 total)
Security audit result
APPROVED WITH OBSERVATIONS (security-auditor). Backward-compat verified independently (no callers read path via message-substring). Tests pin redacted output end-to-end (not just config). No new wire-leak via JSON-RPC error-mapper (Tier 3.5 does NOT serialize `details` field).
3 non-blocking observations tracked for v0.5+ follow-up:
Test plan
OWASP mapping
🤖 Generated with Claude Code