Commit c0df40b
fix(archive): contain archive path to changes dir (prevents escape + data loss)
`openspec archive <name>` did not validate the change name before resolving
and moving the change directory. A crafted name (e.g. `../../../x`) escapes
`openspec/changes`: `path.join` collapses the `..`, the normal path moves the
target out of the tree, and on the Windows/cross-device rename fallback
`moveDirectory` recursively deletes the source via `fs.rm(src, {recursive,
force})` — a data-loss footgun. Every other destructive command already guards
the name; archive was the exception.
Fix adds a path-containment check (resolve the target and verify it stays
inside the changes dir) before any stat/move/rm. This intentionally does NOT
reuse `validateChangeName`, which would reject legitimate date-prefixed change
names (see #1308). Adds a regression test that plants a sentinel outside
`changes/`, asserts the command rejects, and verifies the sentinel is
untouched.
Related: #1308 (flags the same missing validation as a UX inconsistency; this
addresses the unconnected data-loss angle).
Co-authored-by: Sōren Vale <soren@tessara.us>1 parent 93e27a7 commit c0df40b
2 files changed
Lines changed: 58 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
221 | 221 | | |
222 | 222 | | |
223 | 223 | | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
224 | 241 | | |
225 | 242 | | |
226 | 243 | | |
| |||
491 | 508 | | |
492 | 509 | | |
493 | 510 | | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
494 | 524 | | |
495 | 525 | | |
496 | 526 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
323 | 323 | | |
324 | 324 | | |
325 | 325 | | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
326 | 354 | | |
327 | 355 | | |
328 | 356 | | |
| |||
0 commit comments