Observation
`apps/registry/src/routes/v1/bundles.ts:133` defines `validateArtifactFilename` as an ad-hoc per-route check. The equivalent skill filename in `SkillArtifactSchema` (`packages/schemas/src/skill.ts:100`) only enforces `.skill` extension via regex — no path-traversal guard.
Current state:
- Bundle artifact filename: route-level validation, schema only requires non-empty string
- Skill artifact filename: no path-safety validation; only the extension regex
Inconsistent and out of step with the post-PR-#94 pattern (which moved `entry_point` validation to the schema layer).
Risk
Currently non-exploitable for skills:
- Filename is matched against `release.assets[*].name` (GitHub doesn't put path separators in asset names)
- File is downloaded to a `randomUUID()`-named temp path, so traversal can't write outside the temp dir
But:
Proposed approach
Define `SafeFilenameSchema` in `packages/schemas` (similar to but stricter than `SafeRelativePathSchema` — also forbids `/` and `\` since a filename has no path separators). Apply to both bundle announce artifacts and `SkillArtifactSchema.filename`.
Remove the ad-hoc `validateArtifactFilename` in `bundles.ts:133`.
Acceptance criteria
- New `SafeFilenameSchema` exported from `@nimblebrain/mpak-schemas`
- `SkillArtifactSchema.filename` uses it
- Bundle announce artifact filename uses it (route-level helper removed or trivial wrapper)
- Tests cover the standard rejection cases
Observation
`apps/registry/src/routes/v1/bundles.ts:133` defines `validateArtifactFilename` as an ad-hoc per-route check. The equivalent skill filename in `SkillArtifactSchema` (`packages/schemas/src/skill.ts:100`) only enforces `.skill` extension via regex — no path-traversal guard.
Current state:
Inconsistent and out of step with the post-PR-#94 pattern (which moved `entry_point` validation to the schema layer).
Risk
Currently non-exploitable for skills:
But:
Proposed approach
Define `SafeFilenameSchema` in `packages/schemas` (similar to but stricter than `SafeRelativePathSchema` — also forbids `/` and `\` since a filename has no path separators). Apply to both bundle announce artifacts and `SkillArtifactSchema.filename`.
Remove the ad-hoc `validateArtifactFilename` in `bundles.ts:133`.
Acceptance criteria