Why
A name value like ../../../tmp/evil in hub.toml is accepted as-is and then joined directly onto repos_dir(), silently writing repo contents outside ~/.hub/repos/.
Current state
config/src/toml.rs deserializes Project.name as a free-form String with no format validation. The value is consumed at workflows/src/fetch.rs:47 (repos_dir.join(name)) and workflows/src/implement.rs:166 (repos_dir().join(name)) without any sanitization. A value containing .. components or an absolute path would cause join to escape the intended repos directory — Path::join in Rust follows the OS rules and a segment like ../.. does traverse upward.
Ideal state
name is validated at TOML parse time to match only [a-zA-Z0-9_.-]+ (no /, no .., no absolute paths).
- A
hub.toml entry with an invalid name fails at config load with a clear error naming the offending value, before any filesystem operation runs.
repos_dir().join(name) always resolves to a path strictly inside ~/.hub/repos/.
Out of scope
Starting points
/config/src/toml.rs lines 11–19 — Project struct and name: String field, where validation should be added.
/workflows/src/fetch.rs:47 — repos_dir.join(name) call site.
/workflows/src/implement.rs:166 — repos_dir().join(name) call site.
QA plan
- Add a project entry to
hub.toml with name = "../../../tmp/evil".
- Run
hub fetch — expect a config validation error naming the invalid name, not a directory created outside ~/.hub/repos/.
- Confirm
~/.hub/repos/ contains no new directory and /tmp/evil does not exist.
- Add a project entry with
name = "valid-project_1.0" — expect no error and normal clone behaviour.
- Add a project with
name = "bad/slash" — expect a validation error at config load.
Done when
Project.name is rejected at TOML parse time for any value that contains /, .., or non-[a-zA-Z0-9_.-] characters, confirmed by the QA steps above producing a validation error rather than a filesystem side-effect.
Why
A
namevalue like../../../tmp/evilinhub.tomlis accepted as-is and then joined directly ontorepos_dir(), silently writing repo contents outside~/.hub/repos/.Current state
config/src/toml.rsdeserializesProject.nameas a free-formStringwith no format validation. The value is consumed atworkflows/src/fetch.rs:47(repos_dir.join(name)) andworkflows/src/implement.rs:166(repos_dir().join(name)) without any sanitization. A value containing..components or an absolute path would causejointo escape the intended repos directory —Path::joinin Rust follows the OS rules and a segment like../..does traverse upward.Ideal state
nameis validated at TOML parse time to match only[a-zA-Z0-9_.-]+(no/, no.., no absolute paths).hub.tomlentry with an invalidnamefails at config load with a clear error naming the offending value, before any filesystem operation runs.repos_dir().join(name)always resolves to a path strictly inside~/.hub/repos/.Out of scope
reposlug format (tracked separately in issues GraphQL query injection: validate repo slugs before embedding in query string #76 and Path traversal: validate repo slugs before interpolating into GitHub REST API URLs #77).repos_diritself is resolved.Starting points
/config/src/toml.rslines 11–19 —Projectstruct andname: Stringfield, where validation should be added./workflows/src/fetch.rs:47—repos_dir.join(name)call site./workflows/src/implement.rs:166—repos_dir().join(name)call site.QA plan
hub.tomlwithname = "../../../tmp/evil".hub fetch— expect a config validation error naming the invalidname, not a directory created outside~/.hub/repos/.~/.hub/repos/contains no new directory and/tmp/evildoes not exist.name = "valid-project_1.0"— expect no error and normal clone behaviour.name = "bad/slash"— expect a validation error at config load.Done when
Project.nameis rejected at TOML parse time for any value that contains/,.., or non-[a-zA-Z0-9_.-]characters, confirmed by the QA steps above producing a validation error rather than a filesystem side-effect.