Development record
disembark plan's RenderManifest prints planned file paths unsanitised, so a hostile source-repo filename injects raw C1/bidi terminal escapes into the plan render and its --json twin
issueresolvedminoriss-382
disembark plan's RenderManifest prints planned file paths unsanitised, so a hostile source-repo filename injects raw C1/bidi terminal escapes into the plan render and its --json twin
Evidence
internal/core/lifeboat/plan.go:363—fmt.Fprintf(&b, " %8d %s\n", f.Bytes, f.Path)writes the planned path raw, while the same function sanitisesSourceName(plan.go:361) and both omission fields (plan.go:369), and the sibling rendererembark_render.go:150sanitisesConflict.Path— the one unsanitised renderer of source-repo-derived paths in the package.f.Pathleaves derive from source-repo directory entries viasafeLeaf(plan.go:516-532), which rejects only C0/DEL — not C1 (U+0080–009F), bidi (U+202E) or zero-width. Reached at the terminal viainternal/surface/cli/cli.go:604;fsutil.ValidRelPathis C0/DEL-only too, so nothing narrows it downstream.- Reproduced on a hostile source repo: issue files named with U+202E and U+009B rendered both runes raw in
abcd disembark planoutput; the--jsonbranch leaks them identically (encoding/jsonescapes neither). - Refuter verdict: CONFIRMED (minor, security) — the untrusted-source ruling is the package's own (
coverage.gosanitises the same boundary, pinned bycoverage_test.go:133-160); prior art checked (iss-259, iss-264, iss-340, the sibling round's coverage-renderer fix) — none namesplan.go/RenderManifest.
Frontmatter
| id | iss-382 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | minor |
.abcd/work/issues/resolved/iss-382-disembark-plan-s-rendermanifest-prints.md
open on GitHub ↗ · commit history ↗
mentions4
- dcn-26082000000000082026-08-20 — Bug-hunt round 3 (hunt B), branch bughunt-b/round-3. Baseline
- iss-259intent-store paths render to the terminal unsanitised in several intent verbs: intent plan prints res.Intent.Path and intent create prints the minted path without termsafe.Sanitize, and a hostile clone can commit a drafts/itd-N-<slug>.md whose filename tail carries control bytes (intentFileRe admits anything but a newline after the id), reaching the owner's terminal as escape sequences. The promote render sanitises its two paths as of spc-24; close the class once by sweeping the sibling intent renders through termsafe.Sanitize.
- iss-264cmd/record-lint prints Finding.File and Finding.Message raw — no termsafe.Sanitize and no path scrub on the findings path (its scrubPaths covers only the two error exits), so any rule echoing repo content lets a hostile clone inject terminal escapes into CI logs, and any finding message embedding an os error can leak absolute paths. The abcd CLI surface sanitizes at its findings renderer; the standalone binary does not. Found by the spc-27 security review, which also caught (and the change fixed) the one in-diff instance.
- iss-340abcd history show writes the stored transcript body to the terminal with no termsafe sanitisation (cli.go human render branch) — a transcript that ingested hostile content (fetched pages, target-repo files) replays ESC/CSI/C1/bidi sequences raw; the termsafe sweep missed the history surface