Skip to content

fix(security): close MED Ghost error leak + 3 LOWs from certification audit#67

Merged
tzone85 merged 1 commit into
mainfrom
fix/ghost-error-redact-and-low-followups
Jun 11, 2026
Merged

fix(security): close MED Ghost error leak + 3 LOWs from certification audit#67
tzone85 merged 1 commit into
mainfrom
fix/ghost-error-redact-and-low-followups

Conversation

@tzone85

@tzone85 tzone85 commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Summary

Closes the 1 MED + 3 LOW from the third certification audit on patched main. After this PR all CRITICAL + HIGH + MED categories the auditor checked are clean, and the LOWs are either real fixes or explicit trust-boundary documentation.

# Severity What
1 MED Ghost API error bodies echoed verbatim — DSN fragments and tokens leaked into operator logs
2 LOW `SetTemplateFlag` bool injected via `fmt.Sprintf` (safe today, latent on signature change)
3 LOW SQLite schema-evolution contract relied on convention with no explicit doc block
4 LOW `VXD_SHELL` trust boundary undocumented

Ghost error redaction (MED)

New `safeBody` helper:

  • Caps body at 256 bytes (logs can't be flooded by a verbose HTML error).
  • Redacts URL-form DSN passwords (`postgres://user:pass@…` → `postgres://user:***@…`).
  • Redacts key/value `password=…` → `password=***`.

4 tests: truncation, URL-form redaction, key/value redaction, benign content pass-through.

SetTemplateFlag parameterization (LOW)

`UPDATE pg_database SET datistemplate = $1 WHERE datname = $2` — matches every other query in the file. Safe today via Go typing, but a future refactor that changes `on` from `bool` to `string` can't silently reintroduce SQL injection.

Documentation closures

  • `internal/state/sqlite.go` — new SCHEMA EVOLUTION header making the migrations-slice contract explicit.
  • `internal/shellexec` — new SECURITY / TRUST BOUNDARY block stating VXD_SHELL is NEVER read from `vxd.yaml` so a future YAML wiring will notice the gap.

Test plan

  • `go test ./... -count=1` — all 28 packages green
  • `go vet ./...` clean
  • `go build ./...` clean

The third certification audit on patched main returned 0 CRITICAL,
0 HIGH, 1 MED, 3 LOW. Closes:

1. Ghost API error body leak (MED — internal/devdb/ghost/errors.go).
   wrapHTTPError echoed the full response body into every error
   string. Ghost responses can include DSN fragments and internal
   session tokens; those landed in operator logs and CI archives via
   any caller's `log.Printf`. New `safeBody`:
   - Caps body at 256 bytes so a verbose HTML error can't flood logs.
   - Redacts URL-form DSN passwords (postgres://user:pass@…) to
     `user:***@…`.
   - Redacts key/value `password=…` to `password=***`.
   Four new tests pin truncation, URL redaction, key/value redaction,
   and pass-through of benign content.

2. SetTemplateFlag boolean via fmt.Sprintf (LOW — devdb/docker/pg.go).
   Safe today via Go typing, but a future refactor that changes `on`
   from bool to string could silently reintroduce SQL injection.
   Replaced with `$1`/`$2` placeholders matching every other query in
   the file. Latent issue closed at zero cost.

3. SQLite schema-evolution contract (LOW — internal/state/sqlite.go).
   The audit flagged that CREATE TABLE IF NOT EXISTS alone doesn't
   evolve existing schemas. Existing behaviour already pairs base
   creates with ALTER TABLE ADD COLUMN via tryMigrate (added earlier
   this session); now an explicit SCHEMA EVOLUTION header in the
   comment documents the contract so future contributors know to
   append to the migrations slice rather than rely on IF NOT EXISTS.

4. VXD_SHELL trust boundary documented (LOW — internal/shellexec).
   The audit noted VXD_SHELL substitutes the shell binary entirely
   and an attacker controlling the process environment could redirect
   to an arbitrary binary. New SECURITY / TRUST BOUNDARY block in the
   package doc explicitly states VXD_SHELL is NEVER read from
   `vxd.yaml` — only from the live process environment — so a future
   refactor that wires it to a YAML field will notice the gap.

All 28 packages still pass with -count=1.
@tzone85 tzone85 merged commit e1c54cf into main Jun 11, 2026
4 of 5 checks passed
tzone85 added a commit that referenced this pull request Jun 16, 2026
The third certification audit on patched main returned 0 CRITICAL,
0 HIGH, 1 MED, 3 LOW. Closes:

1. Ghost API error body leak (MED — internal/devdb/ghost/errors.go).
   wrapHTTPError echoed the full response body into every error
   string. Ghost responses can include DSN fragments and internal
   session tokens; those landed in operator logs and CI archives via
   any caller's `log.Printf`. New `safeBody`:
   - Caps body at 256 bytes so a verbose HTML error can't flood logs.
   - Redacts URL-form DSN passwords (postgres://user:pass@…) to
     `user:***@…`.
   - Redacts key/value `password=…` to `password=***`.
   Four new tests pin truncation, URL redaction, key/value redaction,
   and pass-through of benign content.

2. SetTemplateFlag boolean via fmt.Sprintf (LOW — devdb/docker/pg.go).
   Safe today via Go typing, but a future refactor that changes `on`
   from bool to string could silently reintroduce SQL injection.
   Replaced with `$1`/`$2` placeholders matching every other query in
   the file. Latent issue closed at zero cost.

3. SQLite schema-evolution contract (LOW — internal/state/sqlite.go).
   The audit flagged that CREATE TABLE IF NOT EXISTS alone doesn't
   evolve existing schemas. Existing behaviour already pairs base
   creates with ALTER TABLE ADD COLUMN via tryMigrate (added earlier
   this session); now an explicit SCHEMA EVOLUTION header in the
   comment documents the contract so future contributors know to
   append to the migrations slice rather than rely on IF NOT EXISTS.

4. VXD_SHELL trust boundary documented (LOW — internal/shellexec).
   The audit noted VXD_SHELL substitutes the shell binary entirely
   and an attacker controlling the process environment could redirect
   to an arbitrary binary. New SECURITY / TRUST BOUNDARY block in the
   package doc explicitly states VXD_SHELL is NEVER read from
   `vxd.yaml` — only from the live process environment — so a future
   refactor that wires it to a YAML field will notice the gap.

All 28 packages still pass with -count=1.

Co-authored-by: Thando Mini <tzone85@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant