Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

Release cut for the Aug adopt/webui hardening + 2 residual staging gaps

Aperta
#288 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
5/5
Tempo stimato
Più di una settimana
Idoneità per principianti
42/100
Tipo di issue
Bug
Chiarezza
Abbastanza chiara
Stato di attività
Attiva
Stack tecnologico
python
Ambito
release, security

Direzione di ricerca

Read skillopt_sleep/staging.py, especially _live_target_within_roots, the legacy-pair block around L3058-L3130, and _planned_live_directories; reproduce the documented adopt command against the staging layout. Review pyproject.toml for the release version. Done means the legacy path is constrained like the per-skill path, unprovenance staging is rejected, and the released artifact includes the hardened tree.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Two asks: a release cut, and two residual staging gaps the August hardening left open.

Ask 1 — cut a release

PyPI skillopt latest is 0.2.0, released 2026-07-02, with no release since (checked today; pyproject.toml at main still says 0.2.0). Since that wheel was built, main merged:

  • #212 (2026-08-20) — safe subset adoption machinery,
  • #241 (2026-08-20) — the staging-adopt hardening: _safe_live_path, sha256 pins, realpath identity, symlink/junction rejection, per-skill roots containment,
  • #249 (2026-08-29) — webui security hardening (loopback default bind among it).

So pip install skillopt today installs an adopt() with none of #241's checks and a webui that binds 0.0.0.0 by default — while the fixes have been merged for a month. #117 (closed) already showed the practical cost of this exact lag: a wheel-content bug fixed at HEAD that kept biting every fresh install. A 0.2.1 / 0.3.0 cut would make the installable artifact match the hardened tree.

Ask 2 — two residual gaps at HEAD (main @ 79124b37, skillopt_sleep/staging.py blob 615c5ac8cf4c70ccb93e0283e8f7b1439890b6e2)

(a) the legacy SKILL/CLAUDE pair adopt still has no roots containment

_live_target_within_roots (L368 — its own docstring: "the manifest is not a trust boundary") is called on exactly one path: the per-skill adoption (L1508). The legacy-pair block at the bottom of the file (L~3058-3130) validates pins, symlinks/hardlinks, and realpath identity — but never checks the live target against the roots recorded when the night was staged, and _planned_live_directories (L3069) will create parent directories for an absent target at any absolute *.md path the manifest names. The per-skill path gained exactly this containment in #241; the legacy pair didn't. Suggested: re-derive legacy live targets from current config the way the per-skill path does, and run the same _live_target_within_roots check there.

(b) staging has no provenance marker — a staging tree that arrives with a cloned repo is indistinguishable from engine output

Staging lives at <project>/.skillopt-sleep/staging (L829) — project-relative. Nothing about a staging night records that the local engine produced it: the manifest format, file layout, and sha256 pins are all self-consistent and documented, and adopt verifies self-consistency only. #241's threat model (from the PR body) was locally generated, then tampered — "it needs local write access to the staging manifest". The repo-shipped vector needs no local tampering: a cloned project can carry a ready-made .skillopt-sleep/staging/<night>/ and a README/CONTRIBUTING line like "run skillopt-sleep adopt to apply our pre-staged tuned skills". On the current 0.2.0 wheel that is an arbitrary-destination write of repo-authored bytes (verified below); at HEAD the per-skill path confines it to the skills roots, but the legacy pair (gap a) does not.

Suggested: a provenance marker the repo cannot forge — a state-side registry of staged nights (outside the project, e.g. under the sleep state dir), or an HMAC of the manifest keyed by a sleep-state secret — so adopt rejects staging that did not originate from the local engine.

Reproduction on the current wheel (0.2.0)

A scratch clone containing only .skillopt-sleep/staging/20260920-120000/{manifest.json, proposed_SKILL.md, proposed_CLAUDE.md}, with the manifest's live_skill_path set to ../../../../../staged-outside-repo.md, then the product's own documented apply command in that directory:

$ skillopt-sleep adopt
[sleep] adopted from <clone>\.skillopt-sleep\staging\20260920-120000
   -> ../../../../../staged-outside-repo.md
   -> ./CLAUDE.md

Result: the repo-authored bytes land at the drive root (the overlong ../ chain clamps there on Windows — destination independent of clone depth and username), plus the clone's own CLAUDE.md is overwritten. With an absolute live_skill_path under <home>\.claude\skills\<name>\SKILL.md, the bytes land in the user-global skills root that Claude Code auto-loads in every future session, every project. The wheel's adopt() core (staging.py L137-161 in 0.2.0) is makedirs + shutil.copy2 on the manifest value — no path safety, no pins, no authorship check. At HEAD these specific writes are contained by #241 on the per-skill path (which is why the release cut matters first); the residual is gaps (a) and (b) above.

Happy to help with either a release checklist or a PR for the legacy containment + provenance marker.

Lingua principale
Python
Stelle
17.3k
Fork
1.6k
Merge medio
2g 23h
PR unite (30g)
9

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di microsoft/SkillOpt

Tutte le issue di microsoft/SkillOpt

Issue simili

Altre issue su Python

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.