Skip to content

fix: declare dashboard deps extra + harden template-name path traversal - #4

Open
kochj23 wants to merge 2 commits into
dioptx:mainfrom
kochj23:fix/deps-and-template-traversal
Open

kochj23 wants to merge 2 commits into
dioptx:mainfrom
kochj23:fix/deps-and-template-traversal

Conversation

@kochj23

@kochj23 kochj23 commented Oct 24, 2026

Copy link
Copy Markdown

What

Two focused bug fixes, each with regression tests.

1. Undeclared dashboard runtime dependencies (packaging)

core/dashboard.py imports textual and rich, but pyproject.toml declared dependencies = []. On a clean install, weft dashboard — and even import core.dashboard — hard-failed with ModuleNotFoundError.

  • Declared the two libraries as an optional extra: pip install weft[dashboard]. This keeps the core engine stdlib-only (per CONTRIBUTING.md: "The Python core is stdlib only"), confining the only third-party deps to the one feature that needs them.
  • Guarded the imports so the module still loads without them (used a TYPE_CHECKING block so type checkers still see the real symbols).
  • The dashboard entrypoint now exits with an actionable message (pip install weft[dashboard]) instead of a traceback when the extra is missing.
  • Pinned sensible lower bounds: textual>=0.40, rich>=13.0.

2. Template-name path traversal (security)

templates.load_template / save_template interpolated the caller-supplied name straight into d / f"{name}.json" with no sanitization, so a name like ../../evil escaped the templates directory on both read and write.

  • Added a _safe_name helper that rejects anything that isn't a single safe stem (path separators, .., absolute paths, leading dots). Legitimate names (generic, feature-workflow, …) behave exactly as before.
  • The weft start / preview CLI paths catch the resulting ValueError and exit cleanly rather than surfacing a traceback.

Why

Both are reachable on a normal clean install / with normal CLI input: the dashboard is simply unusable without its deps declared, and the template name is attacker-influenced input that could read or clobber files outside the intended directory.

Tests

Added 23 tests across the project's pytest style, labelled by category:

  • security: traversal + absolute-path names rejected on load and save; nothing escapes the sandbox.
  • unit: _safe_name accept/reject table.
  • functional: legitimate names + save→load roundtrip unaffected; dashboard import guard fires with the helpful message.
  • integration: weft start / weft preview reject a bad name with a clean exit (no traceback).
  • frame / packaging: the dashboard extra is declared with textual + rich; core dependencies stays empty; core.dashboard imports even when the deps are absent.

Test plan / results

  • pytest -q — baseline 270 passed before changes; 293 passed after (23 new).
  • Verified on Python 3.14 locally; packaging test is written to also work on 3.10 (no tomllib dependency there).
  • pyright on the changed files: core/templates.py clean; core/dashboard.py reduced from 53 errors (my first fallback approach) to the same 4 pre-existing reportMissingImports the file already had when textual isn't installed (fully clean once textual is present); core/cli.py unchanged error count (2 pre-existing, unrelated).

🤖 Generated with Claude Code

Jordan Koch and others added 2 commits October 24, 2026 14:06
core/dashboard.py imports textual and rich, but pyproject.toml declared no
runtime dependencies, so `weft dashboard` (and even `import core.dashboard`)
hard-failed with ModuleNotFoundError on a clean install.

Declare the two libraries as an optional `dashboard` extra
(`pip install weft[dashboard]`), keeping the core engine stdlib-only per
CONTRIBUTING. Guard the imports so the module still loads without them, and
have the dashboard entrypoint exit with an actionable
`pip install weft[dashboard]` message instead of a traceback.

Tests: packaging (extra declared, core deps stay empty), frame (module imports
with deps absent), functional (import guard fires with helpful message).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
load_template/save_template interpolated the caller-supplied name straight into
`<dir>/<name>.json`, so a name like `../../evil` escaped the templates directory
on both read and write.

Validate names to a single safe stem via a new `_safe_name` helper (rejects path
separators, `..`, absolute paths and leading dots). Legitimate names behave
exactly as before. The `weft start` / `preview` CLI paths catch the ValueError
and exit cleanly instead of surfacing a traceback.

Tests: unit (`_safe_name` accept/reject), security (load/save reject traversal +
absolute paths, nothing escapes the sandbox), functional (legit names + roundtrip
unaffected), integration (CLI start/preview reject a bad name cleanly).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.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